diff --git a/README.md b/README.md index 51c4aa5e8..b3dab832c 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,15 @@ RageFrame创建于2016年4月16日,一个基于Yii2高级框架的快速开发 - 极致的插件机制,系统内的系统,安装和卸载不会对原来的系统产生影响,强大的功能完全满足各阶段的需求,支持用户多端访问(后台、微信、Api、前台等),系统中的系统。 - 极完善的RBAC权限控制管理、无限父子级权限分组、可自由分配子级权限,且按钮/链接/自定义内容/插件等都可加入权限控制。 - 只做基础底层内容,不会在上面开发过多的业务内容,满足绝大多数的系统二次开发。 -- 多入口模式,多入口分为 backend(后台)、frontend(PC前端),wechat(微信),api(对内接口),oauth2 server(对外接口),不同的业务,不同的设备,进入不同的入口。 +- 多入口模式,多入口分为 backend(后台)、frontend(PC前端)、wechat(微信)、console(控制台)、api(对内接口)、oauth2 server(对外接口),不同的业务,不同的设备,进入不同的入口。 - 对接微信公众号且支持小程序,使用了一款优秀的微信非官方SDK Easywechat 4.x,开箱即用,预置了绝大部分功能,大幅度的提升了微信开发效率。 - 整合了第三方登录,目前有QQ、微信、微博、GitHub等等。 - 整合了第三方支付,目前有微信支付、支付宝支付、银联支付,二次封装为网关多个支付一个入口一个出口。 - 整合了RESTful API,支持前后端分离接口开发和app接口开发,可直接上手开发业务。 - 一键切换云存储,本地存储、腾讯COS、阿里云OOS、七牛云存储都可一键切换,且增加其他第三方存储也非常方便。 -- 全面监控系统报错,报错日志写入数据库,方便定位错误信息。 +- 全面监控系统报错,报错日志写入数据库,方便定位错误信息。支持直接钉钉提醒。 - 快速高效的Servises(服务层),遵循Yii2的懒加载方式,只初始化使用到的组件服务。 -- 丰富的表单控件(时间、日期、时间日期、日期范围选择、颜色选择器、省市区三级联动、省市区勾选、单图上传、多图上传、单文件上传、多文件上传、百度编辑器、图表、多文本编辑框、地图经纬度选择器、图片裁剪上传、TreeGrid)和组件(二维码生成、Curl、IP地址转地区),快速开发,不必在为基础组件而担忧。 +- 丰富的表单控件(时间、日期、时间日期、日期范围选择、颜色选择器、省市区三级联动、省市区勾选、单图上传、多图上传、单文件上传、多文件上传、百度编辑器、百度图表、多文本编辑框、地图经纬度选择器、图片裁剪上传、TreeGrid、JsTree)和组件(二维码生成、Curl、IP地址转地区),快速开发,不必在为基础组件而担忧。 - 完善的文档和辅助类,方便二次开发与集成。 ### 思维导图 @@ -38,14 +38,18 @@ RageFrame创建于2016年4月16日,一个基于Yii2高级框架的快速开发 ![image](docs/guide-zh-CN/images/sys-config.png) 【系统 - 角色编辑】 ![image](docs/guide-zh-CN/images/rbac-role.png) +【系统 - 日志统计】 +![image](docs/guide-zh-CN/images/log-stat.png) +【会员 - 信息】 +![image](docs/guide-zh-CN/images/member-info.png) 【微信 - 自定义菜单】 ![image](docs/guide-zh-CN/images/wechat-menu.png) -【微信 - 关注统计】 -![image](docs/guide-zh-CN/images/wechat-stat.png) 【插件模块 - 列表】 ![image](docs/guide-zh-CN/images/addon-list.png) 【插件模块 - 文章模块】 ![image](docs/guide-zh-CN/images/addon-activity.png) +【插件模块 - 系统监控】 +![image](docs/guide-zh-CN/images/system.png) ### 开始之前 diff --git a/addons/RfArticle/Upgrade.php b/addons/RfArticle/Upgrade.php index 3bfac39b6..aa7de5313 100644 --- a/addons/RfArticle/Upgrade.php +++ b/addons/RfArticle/Upgrade.php @@ -1,4 +1,5 @@ version) - { + switch ($addon->version) { case '1.0.1' : // 增加测试 - 冗余的字段 // $migration->addColumn('{{%addon_example_curd}}', 'redundancy_field', 'varchar(48)'); diff --git a/addons/RfArticle/api/controllers/AdvController.php b/addons/RfArticle/api/controllers/AdvController.php index 7355c1e5f..973b732af 100644 --- a/addons/RfArticle/api/controllers/AdvController.php +++ b/addons/RfArticle/api/controllers/AdvController.php @@ -1,4 +1,5 @@ [ [ - 'title' => '首页入口', - 'route' => 'index/index', + 'title' => '幻灯片管理', + 'route' => 'adv/index', 'icon' => '' - ] + ], + [ + 'title' => '文章分类管理', + 'route' => 'article-cate/index', + 'icon' => '' + ], + [ + 'title' => '文章管理', + 'route' => 'article/index', + 'icon' => '' + ], ], // ----------------------- 菜单配置 ----------------------- // diff --git a/addons/RfArticle/common/models/Adv.php b/addons/RfArticle/common/models/Adv.php index 37ce51da1..b471e9ed6 100644 --- a/addons/RfArticle/common/models/Adv.php +++ b/addons/RfArticle/common/models/Adv.php @@ -1,4 +1,5 @@ where(['<', 'id', $id]) ->andWhere(['merchant_id' => Yii::$app->services->merchant->getId()]) ->select(['id', 'title']) - ->orderBy('id asc') + ->orderBy('id desc') ->one(); } @@ -180,7 +181,7 @@ protected function getPosition() */ public function getCate() { - return $this->hasOne(ArticleCate::class,['id' => 'cate_id']); + return $this->hasOne(ArticleCate::class, ['id' => 'cate_id']); } /** diff --git a/addons/RfArticle/common/models/ArticleCate.php b/addons/RfArticle/common/models/ArticleCate.php index e87227e32..f17dd7a37 100644 --- a/addons/RfArticle/common/models/ArticleCate.php +++ b/addons/RfArticle/common/models/ArticleCate.php @@ -1,4 +1,5 @@
- +
置顶 diff --git a/addons/RfArticle/wechat/assets/AppAsset.php b/addons/RfArticle/wechat/assets/AppAsset.php index 86354057b..5aa04d491 100644 --- a/addons/RfArticle/wechat/assets/AppAsset.php +++ b/addons/RfArticle/wechat/assets/AppAsset.php @@ -1,4 +1,5 @@ request->isAjax) - { + if (Yii::$app->request->isAjax) { $data = ArticleSingle::find() ->select(['id', 'title', 'cover', 'created_at']) ->where(['status' => StatusEnum::ENABLED]) ->andFilterWhere(['merchant_id' => $this->getMerchantId()]); - $pages = new Pagination(['totalCount' => $data->count(), 'pageSize' => $this->pageSize, 'validatePage' => false]); + $pages = new Pagination([ + 'totalCount' => $data->count(), + 'pageSize' => $this->pageSize, + 'validatePage' => false + ]); $models = $data->offset($pages->offset) ->orderBy('sort asc, id desc') ->limit($pages->limit) ->asArray() ->all(); - foreach ($models as &$model) - { + foreach ($models as &$model) { $model['link'] = Url::to(['detail', 'id' => $model['id']]); $model['created_at'] = date('Y-m-d', $model['created_at']); } diff --git a/addons/RfArticle/wechat/views/index/detail.php b/addons/RfArticle/wechat/views/index/detail.php index 6dcfb88a8..edee6df4b 100644 --- a/addons/RfArticle/wechat/views/index/detail.php +++ b/addons/RfArticle/wechat/views/index/detail.php @@ -1,4 +1,5 @@ title = '文章详情'; @@ -7,9 +8,8 @@

时间:formatter->asDate($model['created_at']); ?>
-
- +

diff --git a/addons/RfArticle/wechat/views/index/index.php b/addons/RfArticle/wechat/views/index/index.php index 469fc540a..193b9c53d 100644 --- a/addons/RfArticle/wechat/views/index/index.php +++ b/addons/RfArticle/wechat/views/index/index.php @@ -1,4 +1,5 @@
- +

{{item.title}}

{{item.created_at}}

@@ -25,27 +26,27 @@ \ No newline at end of file diff --git a/addons/RfDevTool/common/config/backend.php b/addons/RfDevTool/common/config/backend.php index 7b6074323..61470de2d 100644 --- a/addons/RfDevTool/common/config/backend.php +++ b/addons/RfDevTool/common/config/backend.php @@ -37,6 +37,13 @@ 'params' => [ ], ], + [ + 'title' => '时间戳转换', + 'route' => 'timestamp/index', + 'icon' => '', + 'params' => [ + ], + ], [ 'title' => 'PHP信息', 'route' => 'php-info/index', diff --git a/addons/RfExample/backend/views/curd/content.php b/addons/RfExample/backend/views/curd/_form.php similarity index 98% rename from addons/RfExample/backend/views/curd/content.php rename to addons/RfExample/backend/views/curd/_form.php index bdf218729..60a3bbe97 100644 --- a/addons/RfExample/backend/views/curd/content.php +++ b/addons/RfExample/backend/views/curd/_form.php @@ -137,6 +137,7 @@ field($model, 'content')->widget(\common\widgets\ueditor\UEditor::class, [ 'formData' => [ 'drive' => 'local', // 默认本地 支持qiniu/oss/cos 上传 + 'poster' => false, // 上传视频时返回视频封面图,开启此选项需要安装 ffmpeg 命令 'thumb' => [ [ 'width' => 100, diff --git a/addons/RfExample/backend/views/curd/edit.php b/addons/RfExample/backend/views/curd/edit.php index b1fdfeff8..9fd469c99 100644 --- a/addons/RfExample/backend/views/curd/edit.php +++ b/addons/RfExample/backend/views/curd/edit.php @@ -16,7 +16,7 @@

基本信息

- render('./content', [ + render('./_form', [ 'model' => $model, 'form' => $form, ]); ?> diff --git a/addons/RfExample/backend/views/grid-curd/edit.php b/addons/RfExample/backend/views/grid-curd/edit.php index f4ac9f6e6..ae03ead29 100644 --- a/addons/RfExample/backend/views/grid-curd/edit.php +++ b/addons/RfExample/backend/views/grid-curd/edit.php @@ -10,7 +10,7 @@
- render('../curd/content', [ + render('../curd/_form', [ 'model' => $model, 'form' => $form, ]); ?> diff --git a/addons/RfExample/backend/views/mongo-db-curd/index.php b/addons/RfExample/backend/views/mongo-db-curd/index.php index 8a5a7fe64..1e704f209 100644 --- a/addons/RfExample/backend/views/mongo-db-curd/index.php +++ b/addons/RfExample/backend/views/mongo-db-curd/index.php @@ -34,9 +34,9 @@ - $model['_id']]); ?> + $model['_id']]); ?> - $model['_id']]); ?> + $model['_id']]); ?> diff --git a/addons/RfExample/common/models/MongoDbCurd.php b/addons/RfExample/common/models/MongoDbCurd.php index 83415f471..e243febc6 100644 --- a/addons/RfExample/common/models/MongoDbCurd.php +++ b/addons/RfExample/common/models/MongoDbCurd.php @@ -1,9 +1,10 @@ 'intval'], [['author'], 'safe'], ]; @@ -60,6 +63,7 @@ public function attributeLabels() { return [ '_id' => 'ID', + 'merchant_id' => '商户ID', 'title' => '标题', 'sort' => '排序', 'status' => '状态', @@ -71,20 +75,4 @@ public function attributeLabels() 'updated_at' => '更新时间', ]; } - - /** - * @return array - */ - public function behaviors() - { - return [ - [ - 'class' => TimestampBehavior::class, - 'attributes' => [ - ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], - ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'], - ], - ], - ]; - } } \ No newline at end of file diff --git a/addons/RfHelpers/backend/controllers/DataBaseController.php b/addons/RfHelpers/backend/controllers/DataBaseController.php index b72efd2ca..b2f1af13d 100644 --- a/addons/RfHelpers/backend/controllers/DataBaseController.php +++ b/addons/RfHelpers/backend/controllers/DataBaseController.php @@ -81,6 +81,11 @@ public function actionExport() // 读取备份配置 $config = $this->config; + // 检查备份目录是否可写 + if (!is_writeable($config['path'])) { + return ResultDataHelper::json(404, '备份目录不存在或不可写,请检查后重试!'); + } + // 检查是否有正在执行的任务 $lock = $config['path'] . $config['lock']; if (is_file($lock)) { @@ -90,11 +95,6 @@ public function actionExport() // 创建锁文件 file_put_contents($lock, time()); - // 检查备份目录是否可写 - if (!is_writeable($config['path'])) { - return ResultDataHelper::json(404, '备份目录不存在或不可写,请检查后重试!'); - } - // 生成备份文件信息 $file = [ 'name' => date('Ymd-His', time()), diff --git a/addons/RfHelpers/backend/views/data-base/backups.php b/addons/RfHelpers/backend/views/data-base/backups.php index 72961ad46..cd609d023 100644 --- a/addons/RfHelpers/backend/views/data-base/backups.php +++ b/addons/RfHelpers/backend/views/data-base/backups.php @@ -229,9 +229,8 @@ function repair() { if (this.checked) { $("#list :checkbox").prop("checked", true); } else { - $("#list :checkbox").attr("checked", false); + $("#list :checkbox").prop("checked", false); } }); - }) \ No newline at end of file diff --git a/addons/RfMerchants/backend/controllers/RoleController.php b/addons/RfMerchants/backend/controllers/RoleController.php index 8752f5a8f..efddeb6c4 100644 --- a/addons/RfMerchants/backend/controllers/RoleController.php +++ b/addons/RfMerchants/backend/controllers/RoleController.php @@ -3,13 +3,13 @@ namespace addons\RfMerchants\backend\controllers; use Yii; +use yii\data\ActiveDataProvider; use common\helpers\ArrayHelper; use common\components\Curd; use common\models\common\AuthRole; use common\enums\AppEnum; use common\enums\AuthTypeEnum; use common\helpers\ResultDataHelper; -use yii\data\ActiveDataProvider; /** * Class RoleController @@ -55,13 +55,6 @@ public function actionIndex() 'pagination' => false ]); - foreach ($childRoles as &$childRole) { - $role_id = $role['id'] ?? 0; - if ($childRole['pid'] == $role_id) { - $childRole['pid'] = 0; - }; - } - $dataProvider->setModels($childRoles); return $this->render('index', [ @@ -92,15 +85,36 @@ public function actionEdit() } // 创建角色关联的权限信息 - Yii::$app->services->authRole->accredit($model->id, $data['userTreeIds'] ?? [], AuthTypeEnum::TYPE_DEFAULT); - Yii::$app->services->authRole->accredit($model->id, $data['plugTreeIds'] ?? [], AuthTypeEnum::TYPE_ADDONS); + Yii::$app->services->authRole->accredit($model->id, $data['userTreeIds'] ?? [], AuthTypeEnum::TYPE_DEFAULT, $this->appId); + Yii::$app->services->authRole->accredit($model->id, $data['plugTreeIds'] ?? [], AuthTypeEnum::TYPE_ADDONS, $this->appId); return ResultDataHelper::json(200, '提交成功'); } // 获取当前角色权限 - list($defaultFormAuth, $defaultCheckIds, $addonsFormAuth, $addonsCheckIds) = Yii::$app->services->authRole->getJsTreeData($id); + list($defaultFormAuth, $defaultCheckIds, $addonsFormAuth, $addonsCheckIds) = Yii::$app->services->authRole->getJsTreeData($id, $this->appId); + + return $this->render($this->action->id, [ + 'model' => $model, + 'defaultFormAuth' => $defaultFormAuth, + 'defaultCheckIds' => $defaultCheckIds, + 'addonsFormAuth' => $addonsFormAuth, + 'addonsCheckIds' => $addonsCheckIds, + 'dropDownList' => $this->getDropDownList($id), + 'merchant_id' => $this->merchant_id + ]); + } + /** + * 获取上级角色 + * + * 注意:如果是其他应用则需要自行获取上级 + * + * @param $id + * @return array + */ + protected function getDropDownList($id) + { // 获取父级 $role = Yii::$app->services->authRole->getRole(); $childRoles = Yii::$app->services->authRole->getChildList($this->appId, $role); @@ -112,19 +126,10 @@ public function actionEdit() } $dropDownList = ArrayHelper::itemsMerge($childRoles, $role['pid'] ?? 0); - $dropDownList = ArrayHelper::map(ArrayHelper::itemsMergeDropDown($dropDownList, 'id', 'title', - $role['level'] ?? 1), 'id', 'title'); + $dropDownList = ArrayHelper::map(ArrayHelper::itemsMergeDropDown($dropDownList, 'id', 'title', $role['level'] ?? 1), 'id', 'title'); Yii::$app->services->auth->isSuperAdmin() && $dropDownList = ArrayHelper::merge([0 => '顶级角色'], $dropDownList); - return $this->render($this->action->id, [ - 'model' => $model, - 'defaultFormAuth' => $defaultFormAuth, - 'defaultCheckIds' => $defaultCheckIds, - 'addonsFormAuth' => $addonsFormAuth, - 'addonsCheckIds' => $addonsCheckIds, - 'dropDownList' => $dropDownList, - 'merchant_id' => $this->merchant_id - ]); + return $dropDownList; } /** diff --git a/addons/RfMerchants/backend/views/role/edit.php b/addons/RfMerchants/backend/views/role/edit.php index 3c249ee5f..a9dd9c277 100644 --- a/addons/RfMerchants/backend/views/role/edit.php +++ b/addons/RfMerchants/backend/views/role/edit.php @@ -1,8 +1,8 @@ title = $model->isNewRecord ? '创建' : '编辑'; $this->params['breadcrumbs'][] = ['label' => '商户管理', 'url' => ['merchant/index']]; @@ -10,8 +10,6 @@ $this->params['breadcrumbs'][] = ['label' => $this->title]; ?> - -
@@ -26,11 +24,23 @@
field($model, 'pid')->dropDownList($dropDownList) ?> field($model, 'title')->textInput(); ?> + field($model, 'status')->radioList(StatusEnum::$listExplain); ?> field($model, 'sort')->textInput(); ?> - field($model, 'status')->radioList(StatusEnum::$listExplain); ?>
-
-
+
+ "userTree", + 'defaultData' => $defaultFormAuth, + 'selectIds' => $defaultCheckIds, + ])?> +
+
+ "plugTree", + 'defaultData' => $addonsFormAuth, + 'selectIds' => $addonsCheckIds, + ])?> +
- - -render('_footer')?> +render('_footer') ?> endBody() ?> diff --git a/backend/views/layouts/default.php b/backend/views/layouts/default.php index 657eab5cc..87ebcf4db 100644 --- a/backend/views/layouts/default.php +++ b/backend/views/layouts/default.php @@ -1,31 +1,35 @@ beginPage() ?> - - - - - - - - <?= Html::encode($this->title) ?> - head() ?> - -beginBody() ?> - - -render('_footer')?> -endBody() ?> - + + + + + + + + <?= Html::encode($this->title) ?> + head() ?> + + beginBody() ?> + + + render('_footer') ?> + endBody() ?> + endPage() ?> \ No newline at end of file diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php index b5fc623bf..a1f0102f9 100644 --- a/backend/views/layouts/main.php +++ b/backend/views/layouts/main.php @@ -1,11 +1,14 @@ beginPage() ?> @@ -25,14 +28,14 @@
刷新 - request->referrer != Yii::$app->request->hostInfo . Yii::$app->request->getBaseUrl() . '/'){ ?> + request->referrer != Yii::$app->request->hostInfo . Yii::$app->request->getBaseUrl() . '/') { ?> 返回 'ol', - 'homeLink'=>[ + 'homeLink' => [ 'label' => '' . Yii::$app->params['adminAcronym'], 'url' => "", ], @@ -51,10 +54,10 @@ let config = { tag: "debris->config('sys_tags') ?? false; ?>", isMobile: "params['isMobile'] ?? false; ?>", - emojiBaseUrl : "request->baseUrl ?>/resources/dist/img/emoji/", + emojiBaseUrl: "request->baseUrl ?>/resources/dist/img/emoji/", }; - render('_footer')?> + render('_footer') ?> endBody() ?> diff --git a/backend/views/main/clear-cache.php b/backend/views/main/clear-cache.php index eae030ebc..f02b3bc04 100644 --- a/backend/views/main/clear-cache.php +++ b/backend/views/main/clear-cache.php @@ -28,4 +28,4 @@
-
+
\ No newline at end of file diff --git a/backend/views/main/index.php b/backend/views/main/index.php index 935565152..19159958d 100644 --- a/backend/views/main/index.php +++ b/backend/views/main/index.php @@ -2,6 +2,8 @@ use common\helpers\Html; use backend\assets\AppAsset; +/* @var $this yii\web\View */ + AppAsset::register($this); ?> beginPage() ?> diff --git a/backend/widgets/Alert.php b/backend/widgets/Alert.php index 48ed57fe6..3a4ab38ab 100644 --- a/backend/widgets/Alert.php +++ b/backend/widgets/Alert.php @@ -68,7 +68,7 @@ public function run() // 调用函数 $this->view->registerJs(<<level = (int) $this->level; + $this->level = (int)$this->level; $this->level < 1 && $this->level = 1; $this->level > 3 && $this->level = 3; } @@ -93,7 +94,7 @@ public function run() $areaName = $this->areaName; $provinceIds = StringHelper::parseAttr($this->model->$provincesName); - $cityIds = $this->level >= 2 ? StringHelper::parseAttr($this->model->$cityName) : []; + $cityIds = $this->level >= 2 ? StringHelper::parseAttr($this->model->$cityName) : []; $areaIds = $this->level == 3 ? StringHelper::parseAttr($this->model->$areaName) : []; // 获取选中数据 @@ -114,4 +115,5 @@ public function run() ]); } } + ?> \ No newline at end of file diff --git a/backend/widgets/area/views/area.php b/backend/widgets/area/views/area.php index 6162e3e1d..2640cf3dc 100644 --- a/backend/widgets/area/views/area.php +++ b/backend/widgets/area/views/area.php @@ -1,36 +1,39 @@ field($model, $provincesName)->hiddenInput(['id' => 'provinceIds'])->label(false) ?> = 2) { echo $form->field($model, $cityName)->hiddenInput(['id' => 'cityIds'])->label(false); -}?> +} ?> = 3) { echo $form->field($model, $areaName)->hiddenInput(['id' => 'areaIds'])->label(false); -}?> +} ?> ' + - '
'; - - this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl); - this.container = $(options.template).appendTo(this.parentEl); - - // - // handle all the possible options overriding defaults - // - - if (typeof options.locale === 'object') { - - if (typeof options.locale.direction === 'string') - this.locale.direction = options.locale.direction; - - if (typeof options.locale.format === 'string') - this.locale.format = options.locale.format; - - if (typeof options.locale.separator === 'string') - this.locale.separator = options.locale.separator; - - if (typeof options.locale.daysOfWeek === 'object') - this.locale.daysOfWeek = options.locale.daysOfWeek.slice(); - - if (typeof options.locale.monthNames === 'object') - this.locale.monthNames = options.locale.monthNames.slice(); - - if (typeof options.locale.firstDay === 'number') - this.locale.firstDay = options.locale.firstDay; - - if (typeof options.locale.applyLabel === 'string') - this.locale.applyLabel = options.locale.applyLabel; - - if (typeof options.locale.cancelLabel === 'string') - this.locale.cancelLabel = options.locale.cancelLabel; - - if (typeof options.locale.weekLabel === 'string') - this.locale.weekLabel = options.locale.weekLabel; - - if (typeof options.locale.customRangeLabel === 'string'){ - //Support unicode chars in the custom range name. - var elem = document.createElement('textarea'); - elem.innerHTML = options.locale.customRangeLabel; - var rangeHtml = elem.value; - this.locale.customRangeLabel = rangeHtml; - } - } - this.container.addClass(this.locale.direction); - - if (typeof options.startDate === 'string') - this.startDate = moment(options.startDate, this.locale.format); - - if (typeof options.endDate === 'string') - this.endDate = moment(options.endDate, this.locale.format); - - if (typeof options.minDate === 'string') - this.minDate = moment(options.minDate, this.locale.format); - - if (typeof options.maxDate === 'string') - this.maxDate = moment(options.maxDate, this.locale.format); - - if (typeof options.startDate === 'object') - this.startDate = moment(options.startDate); - - if (typeof options.endDate === 'object') - this.endDate = moment(options.endDate); - - if (typeof options.minDate === 'object') - this.minDate = moment(options.minDate); - - if (typeof options.maxDate === 'object') - this.maxDate = moment(options.maxDate); - - // sanity check for bad options - if (this.minDate && this.startDate.isBefore(this.minDate)) - this.startDate = this.minDate.clone(); - - // sanity check for bad options - if (this.maxDate && this.endDate.isAfter(this.maxDate)) - this.endDate = this.maxDate.clone(); - - if (typeof options.applyClass === 'string') - this.applyClass = options.applyClass; - - if (typeof options.cancelClass === 'string') - this.cancelClass = options.cancelClass; - - if (typeof options.dateLimit === 'object') - this.dateLimit = options.dateLimit; - - if (typeof options.opens === 'string') - this.opens = options.opens; - - if (typeof options.drops === 'string') - this.drops = options.drops; - - if (typeof options.showWeekNumbers === 'boolean') - this.showWeekNumbers = options.showWeekNumbers; - - if (typeof options.showISOWeekNumbers === 'boolean') - this.showISOWeekNumbers = options.showISOWeekNumbers; - - if (typeof options.buttonClasses === 'string') - this.buttonClasses = options.buttonClasses; - - if (typeof options.buttonClasses === 'object') - this.buttonClasses = options.buttonClasses.join(' '); - - if (typeof options.showDropdowns === 'boolean') - this.showDropdowns = options.showDropdowns; - - if (typeof options.showCustomRangeLabel === 'boolean') - this.showCustomRangeLabel = options.showCustomRangeLabel; - - if (typeof options.singleDatePicker === 'boolean') { - this.singleDatePicker = options.singleDatePicker; - if (this.singleDatePicker) - this.endDate = this.startDate.clone(); - } - - if (typeof options.timePicker === 'boolean') - this.timePicker = options.timePicker; - - if (typeof options.timePickerSeconds === 'boolean') - this.timePickerSeconds = options.timePickerSeconds; - - if (typeof options.timePickerIncrement === 'number') - this.timePickerIncrement = options.timePickerIncrement; - - if (typeof options.timePicker24Hour === 'boolean') - this.timePicker24Hour = options.timePicker24Hour; - - if (typeof options.autoApply === 'boolean') - this.autoApply = options.autoApply; - - if (typeof options.autoUpdateInput === 'boolean') - this.autoUpdateInput = options.autoUpdateInput; - - if (typeof options.linkedCalendars === 'boolean') - this.linkedCalendars = options.linkedCalendars; - - if (typeof options.isInvalidDate === 'function') - this.isInvalidDate = options.isInvalidDate; - - if (typeof options.isCustomDate === 'function') - this.isCustomDate = options.isCustomDate; - - if (typeof options.alwaysShowCalendars === 'boolean') - this.alwaysShowCalendars = options.alwaysShowCalendars; - - // update day names order to firstDay - if (this.locale.firstDay != 0) { - var iterator = this.locale.firstDay; - while (iterator > 0) { - this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); - iterator--; - } - } - - var start, end, range; - - //if no start/end dates set, check if an input element contains initial values - if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { - if ($(this.element).is('input[type=text]')) { - var val = $(this.element).val(), - split = val.split(this.locale.separator); - - start = end = null; - - if (split.length == 2) { - start = moment(split[0], this.locale.format); - end = moment(split[1], this.locale.format); - } else if (this.singleDatePicker && val !== "") { - start = moment(val, this.locale.format); - end = moment(val, this.locale.format); - } - if (start !== null && end !== null) { - this.setStartDate(start); - this.setEndDate(end); - } - } - } - - if (typeof options.ranges === 'object') { - for (range in options.ranges) { - - if (typeof options.ranges[range][0] === 'string') - start = moment(options.ranges[range][0], this.locale.format); - else - start = moment(options.ranges[range][0]); - - if (typeof options.ranges[range][1] === 'string') - end = moment(options.ranges[range][1], this.locale.format); - else - end = moment(options.ranges[range][1]); - - // If the start or end date exceed those allowed by the minDate or dateLimit - // options, shorten the range to the allowable period. - if (this.minDate && start.isBefore(this.minDate)) - start = this.minDate.clone(); - - var maxDate = this.maxDate; - if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate)) - maxDate = start.clone().add(this.dateLimit); - if (maxDate && end.isAfter(maxDate)) - end = maxDate.clone(); - - // If the end of the range is before the minimum or the start of the range is - // after the maximum, don't display this range option at all. - if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) - || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day'))) - continue; - - //Support unicode chars in the range names. - var elem = document.createElement('textarea'); - elem.innerHTML = range; - var rangeHtml = elem.value; - - this.ranges[rangeHtml] = [start, end]; - } - - var list = '
    '; - for (range in this.ranges) { - list += '
  • ' + range + '
  • '; - } - if (this.showCustomRangeLabel) { - list += '
  • ' + this.locale.customRangeLabel + '
  • '; - } - list += '
'; - this.container.find('.ranges').prepend(list); - } - - if (typeof cb === 'function') { - this.callback = cb; - } - - if (!this.timePicker) { - this.startDate = this.startDate.startOf('day'); - this.endDate = this.endDate.endOf('day'); - this.container.find('.calendar-time').hide(); - } - - //can't be used together for now - if (this.timePicker && this.autoApply) - this.autoApply = false; - - if (this.autoApply && typeof options.ranges !== 'object') { - this.container.find('.ranges').hide(); - } else if (this.autoApply) { - this.container.find('.applyBtn, .cancelBtn').addClass('hide'); - } - - if (this.singleDatePicker) { - this.container.addClass('single'); - this.container.find('.calendar.left').addClass('single'); - this.container.find('.calendar.left').show(); - this.container.find('.calendar.right').hide(); - this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide(); - if (this.timePicker) { - this.container.find('.ranges ul').hide(); - } else { - this.container.find('.ranges').hide(); - } - } - - if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) { - this.container.addClass('show-calendar'); - } - - this.container.addClass('opens' + this.opens); - - //swap the position of the predefined ranges if opens right - if (typeof options.ranges !== 'undefined' && this.opens == 'right') { - this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() ); - } - - //apply CSS classes and labels to buttons - this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses); - if (this.applyClass.length) - this.container.find('.applyBtn').addClass(this.applyClass); - if (this.cancelClass.length) - this.container.find('.cancelBtn').addClass(this.cancelClass); - this.container.find('.applyBtn').html(this.locale.applyLabel); - this.container.find('.cancelBtn').html(this.locale.cancelLabel); - - // - // event listeners - // - - this.container.find('.calendar') - .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this)) - .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this)) - .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this)) - .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this)) - .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this)) - .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this)) - .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this)) - .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this)) - .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this)) - .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this)) - .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this)) - .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this)) - .on('keydown.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsKeydown, this)); - - this.container.find('.ranges') - .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this)) - .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)) - .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this)) - .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this)) - .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this)); - - if (this.element.is('input') || this.element.is('button')) { - this.element.on({ - 'click.daterangepicker': $.proxy(this.show, this), - 'focus.daterangepicker': $.proxy(this.show, this), - 'keyup.daterangepicker': $.proxy(this.elementChanged, this), - 'keydown.daterangepicker': $.proxy(this.keydown, this) //IE 11 compatibility - }); - } else { - this.element.on('click.daterangepicker', $.proxy(this.toggle, this)); - this.element.on('keydown.daterangepicker', $.proxy(this.toggle, this)); - } - - // - // if attached to a text input, set the initial value - // - - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); - } - - }; - - DateRangePicker.prototype = { - - constructor: DateRangePicker, - - setStartDate: function(startDate) { - if (typeof startDate === 'string') - this.startDate = moment(startDate, this.locale.format); - - if (typeof startDate === 'object') - this.startDate = moment(startDate); - - if (!this.timePicker) - this.startDate = this.startDate.startOf('day'); - - if (this.timePicker && this.timePickerIncrement) - this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - - if (this.minDate && this.startDate.isBefore(this.minDate)) { - this.startDate = this.minDate.clone(); - if (this.timePicker && this.timePickerIncrement) - this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - } - - if (this.maxDate && this.startDate.isAfter(this.maxDate)) { - this.startDate = this.maxDate.clone(); - if (this.timePicker && this.timePickerIncrement) - this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - } - - if (!this.isShowing) - this.updateElement(); - - this.updateMonthsInView(); - }, - - setEndDate: function(endDate) { - if (typeof endDate === 'string') - this.endDate = moment(endDate, this.locale.format); - - if (typeof endDate === 'object') - this.endDate = moment(endDate); - - if (!this.timePicker) - this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second'); - - if (this.timePicker && this.timePickerIncrement) - this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); - - if (this.endDate.isBefore(this.startDate)) - this.endDate = this.startDate.clone(); - - if (this.maxDate && this.endDate.isAfter(this.maxDate)) - this.endDate = this.maxDate.clone(); - - if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) - this.endDate = this.startDate.clone().add(this.dateLimit); - - this.previousRightTime = this.endDate.clone(); - - if (!this.isShowing) - this.updateElement(); - - this.updateMonthsInView(); - }, - - isInvalidDate: function() { - return false; - }, - - isCustomDate: function() { - return false; - }, - - updateView: function() { - if (this.timePicker) { - this.renderTimePicker('left'); - this.renderTimePicker('right'); - if (!this.endDate) { - this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); - } else { - this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled'); - } - } - if (this.endDate) { - this.container.find('input[name="daterangepicker_end"]').removeClass('active'); - this.container.find('input[name="daterangepicker_start"]').addClass('active'); - } else { - this.container.find('input[name="daterangepicker_end"]').addClass('active'); - this.container.find('input[name="daterangepicker_start"]').removeClass('active'); - } - this.updateMonthsInView(); - this.updateCalendars(); - this.updateFormInputs(); - }, - - updateMonthsInView: function() { - if (this.endDate) { - - //if both dates are visible already, do nothing - if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month && - (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) - && - (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) - ) { - return; - } - - this.leftCalendar.month = this.startDate.clone().date(2); - if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) { - this.rightCalendar.month = this.endDate.clone().date(2); - } else { - this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); - } - - } else { - if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) { - this.leftCalendar.month = this.startDate.clone().date(2); - this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); - } - } - if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) { - this.rightCalendar.month = this.maxDate.clone().date(2); - this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month'); - } - }, - - updateCalendars: function() { - - if (this.timePicker) { - var hour, minute, second; - if (this.endDate) { - hour = parseInt(this.container.find('.left .hourselect').val(), 10); - minute = parseInt(this.container.find('.left .minuteselect').val(), 10); - second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; - if (!this.timePicker24Hour) { - var ampm = this.container.find('.left .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - } else { - hour = parseInt(this.container.find('.right .hourselect').val(), 10); - minute = parseInt(this.container.find('.right .minuteselect').val(), 10); - second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; - if (!this.timePicker24Hour) { - var ampm = this.container.find('.right .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - } - this.leftCalendar.month.hour(hour).minute(minute).second(second); - this.rightCalendar.month.hour(hour).minute(minute).second(second); - } - - this.renderCalendar('left'); - this.renderCalendar('right'); - - //highlight any predefined range matching the current start and end dates - this.container.find('.ranges li').removeClass('active'); - if (this.endDate == null) return; - - this.calculateChosenLabel(); - }, - - renderCalendar: function(side) { - - // - // Build the matrix of dates that will populate the calendar - // - - var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar; - var month = calendar.month.month(); - var year = calendar.month.year(); - var hour = calendar.month.hour(); - var minute = calendar.month.minute(); - var second = calendar.month.second(); - var daysInMonth = moment([year, month]).daysInMonth(); - var firstDay = moment([year, month, 1]); - var lastDay = moment([year, month, daysInMonth]); - var lastMonth = moment(firstDay).subtract(1, 'month').month(); - var lastYear = moment(firstDay).subtract(1, 'month').year(); - var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); - var dayOfWeek = firstDay.day(); - - //initialize a 6 rows x 7 columns array for the calendar - var calendar = []; - calendar.firstDay = firstDay; - calendar.lastDay = lastDay; - - for (var i = 0; i < 6; i++) { - calendar[i] = []; - } - - //populate the calendar with date objects - var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; - if (startDay > daysInLastMonth) - startDay -= 7; - - if (dayOfWeek == this.locale.firstDay) - startDay = daysInLastMonth - 6; - - var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); - - var col, row; - for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) { - if (i > 0 && col % 7 === 0) { - col = 0; - row++; - } - calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second); - curDate.hour(12); - - if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') { - calendar[row][col] = this.minDate.clone(); - } - - if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') { - calendar[row][col] = this.maxDate.clone(); - } - - } - - //make the calendar object available to hoverDate/clickDate - if (side == 'left') { - this.leftCalendar.calendar = calendar; - } else { - this.rightCalendar.calendar = calendar; - } - - // - // Display the calendar - // - - var minDate = side == 'left' ? this.minDate : this.startDate; - var maxDate = this.maxDate; - var selected = side == 'left' ? this.startDate : this.endDate; - var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'}; - - var html = ''; - html += ''; - html += ''; - - // add empty cell for week number - if (this.showWeekNumbers || this.showISOWeekNumbers) - html += ''; - - if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) { - html += ''; - } else { - html += ''; - } - - var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); - - if (this.showDropdowns) { - var currentMonth = calendar[1][1].month(); - var currentYear = calendar[1][1].year(); - var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); - var minYear = (minDate && minDate.year()) || (currentYear - 50); - var inMinYear = currentYear == minYear; - var inMaxYear = currentYear == maxYear; - - var monthHtml = '"; - - var yearHtml = ''; - - dateHtml = monthHtml + yearHtml; - } - - html += ''; - if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { - html += ''; - } else { - html += ''; - } - - html += ''; - html += ''; - - // add week number label - if (this.showWeekNumbers || this.showISOWeekNumbers) - html += ''; - - $.each(this.locale.daysOfWeek, function(index, dayOfWeek) { - html += ''; - }); - - html += ''; - html += ''; - html += ''; - - //adjust maxDate to reflect the dateLimit setting in order to - //grey out end dates beyond the dateLimit - if (this.endDate == null && this.dateLimit) { - var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); - if (!maxDate || maxLimit.isBefore(maxDate)) { - maxDate = maxLimit; - } - } - - for (var row = 0; row < 6; row++) { - html += ''; - - // add week number - if (this.showWeekNumbers) - html += ''; - else if (this.showISOWeekNumbers) - html += ''; - - for (var col = 0; col < 7; col++) { - - var classes = []; - - //highlight today's date - if (calendar[row][col].isSame(new Date(), "day")) - classes.push('today'); - - //highlight weekends - if (calendar[row][col].isoWeekday() > 5) - classes.push('weekend'); - - //grey out the dates in other months displayed at beginning and end of this calendar - if (calendar[row][col].month() != calendar[1][1].month()) - classes.push('off'); - - //don't allow selection of dates before the minimum date - if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) - classes.push('off', 'disabled'); - - //don't allow selection of dates after the maximum date - if (maxDate && calendar[row][col].isAfter(maxDate, 'day')) - classes.push('off', 'disabled'); - - //don't allow selection of date if a custom function decides it's invalid - if (this.isInvalidDate(calendar[row][col])) - classes.push('off', 'disabled'); - - //highlight the currently selected start date - if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) - classes.push('active', 'start-date'); - - //highlight the currently selected end date - if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) - classes.push('active', 'end-date'); - - //highlight dates in-between the selected dates - if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate) - classes.push('in-range'); - - //apply custom classes for this date - var isCustom = this.isCustomDate(calendar[row][col]); - if (isCustom !== false) { - if (typeof isCustom === 'string') - classes.push(isCustom); - else - Array.prototype.push.apply(classes, isCustom); - } - - var cname = '', disabled = false; - for (var i = 0; i < classes.length; i++) { - cname += classes[i] + ' '; - if (classes[i] == 'disabled') - disabled = true; - } - if (!disabled) - cname += 'available'; - - html += ''; - - } - html += ''; - } - - html += ''; - html += '
' + dateHtml + '
' + this.locale.weekLabel + '' + dayOfWeek + '
' + calendar[row][0].week() + '' + calendar[row][0].isoWeek() + '' + calendar[row][col].date() + '
'; - - this.container.find('.calendar.' + side + ' .calendar-table').html(html); - - }, - - renderTimePicker: function(side) { - - // Don't bother updating the time picker if it's currently disabled - // because an end date hasn't been clicked yet - if (side == 'right' && !this.endDate) return; - - var html, selected, minDate, maxDate = this.maxDate; - - if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) - maxDate = this.startDate.clone().add(this.dateLimit); - - if (side == 'left') { - selected = this.startDate.clone(); - minDate = this.minDate; - } else if (side == 'right') { - selected = this.endDate.clone(); - minDate = this.startDate; - - //Preserve the time already selected - var timeSelector = this.container.find('.calendar.right .calendar-time div'); - if (timeSelector.html() != '') { - - selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour()); - selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute()); - selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second()); - - if (!this.timePicker24Hour) { - var ampm = timeSelector.find('.ampmselect option:selected').val(); - if (ampm === 'PM' && selected.hour() < 12) - selected.hour(selected.hour() + 12); - if (ampm === 'AM' && selected.hour() === 12) - selected.hour(0); - } - - } - - if (selected.isBefore(this.startDate)) - selected = this.startDate.clone(); - - if (maxDate && selected.isAfter(maxDate)) - selected = maxDate.clone(); - - } - - // - // hours - // - - html = ' '; - - // - // minutes - // - - html += ': '; - - // - // seconds - // - - if (this.timePickerSeconds) { - html += ': '; - } - - // - // AM/PM - // - - if (!this.timePicker24Hour) { - html += ''; - } - - this.container.find('.calendar.' + side + ' .calendar-time div').html(html); - - }, - - updateFormInputs: function() { - - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - return; - - this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format)); - if (this.endDate) - this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format)); - - if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { - this.container.find('button.applyBtn').removeAttr('disabled'); - } else { - this.container.find('button.applyBtn').attr('disabled', 'disabled'); - } - - }, - - move: function() { - var parentOffset = { top: 0, left: 0 }, - containerTop; - var parentRightEdge = $(window).width(); - if (!this.parentEl.is('body')) { - parentOffset = { - top: this.parentEl.offset().top - this.parentEl.scrollTop(), - left: this.parentEl.offset().left - this.parentEl.scrollLeft() - }; - parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; - } - - if (this.drops == 'up') - containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; - else - containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; - this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); - - if (this.opens == 'left') { - this.container.css({ - top: containerTop, - right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), - left: 'auto' - }); - if (this.container.offset().left < 0) { - this.container.css({ - right: 'auto', - left: 9 - }); - } - } else if (this.opens == 'center') { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 - - this.container.outerWidth() / 2, - right: 'auto' - }); - if (this.container.offset().left < 0) { - this.container.css({ - right: 'auto', - left: 9 - }); - } - } else { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left, - right: 'auto' - }); - if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { - this.container.css({ - left: 'auto', - right: 0 - }); - } - } - }, - - show: function(e) { - if (this.isShowing) return; - - // Create a click proxy that is private to this instance of datepicker, for unbinding - this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this); - - // Bind global datepicker mousedown for hiding and - $(document) - .on('mousedown.daterangepicker', this._outsideClickProxy) - // also support mobile devices - .on('touchend.daterangepicker', this._outsideClickProxy) - // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them - .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy) - // and also close when focus changes to outside the picker (eg. tabbing between controls) - .on('focusin.daterangepicker', this._outsideClickProxy); - - // Reposition the picker if the window is resized while it's open - $(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this)); - - this.oldStartDate = this.startDate.clone(); - this.oldEndDate = this.endDate.clone(); - this.previousRightTime = this.endDate.clone(); - - this.updateView(); - this.container.show(); - this.move(); - this.element.trigger('show.daterangepicker', this); - this.isShowing = true; - }, - - hide: function(e) { - if (!this.isShowing) return; - - //incomplete date selection, revert to last values - if (!this.endDate) { - this.startDate = this.oldStartDate.clone(); - this.endDate = this.oldEndDate.clone(); - } - - //if a new date range was selected, invoke the user callback function - if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate)) - this.callback(this.startDate, this.endDate, this.chosenLabel); - - //if picker is attached to a text input, update it - this.updateElement(); - - $(document).off('.daterangepicker'); - $(window).off('.daterangepicker'); - this.container.hide(); - this.element.trigger('hide.daterangepicker', this); - this.isShowing = false; - }, - - toggle: function(e) { - if (this.isShowing) { - this.hide(); - } else { - this.show(); - } - }, - - outsideClick: function(e) { - var target = $(e.target); - // if the page is clicked anywhere except within the daterangerpicker/button - // itself then call this.hide() - if ( - // ie modal dialog fix - e.type == "focusin" || - target.closest(this.element).length || - target.closest(this.container).length || - target.closest('.calendar-table').length - ) return; - this.hide(); - this.element.trigger('outsideClick.daterangepicker', this); - }, - - showCalendars: function() { - this.container.addClass('show-calendar'); - this.move(); - this.element.trigger('showCalendar.daterangepicker', this); - }, - - hideCalendars: function() { - this.container.removeClass('show-calendar'); - this.element.trigger('hideCalendar.daterangepicker', this); - }, - - hoverRange: function(e) { - - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - return; - - var label = e.target.getAttribute('data-range-key'); - - if (label == this.locale.customRangeLabel) { - this.updateView(); - } else { - var dates = this.ranges[label]; - this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format)); - this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format)); - } - - }, - - clickRange: function(e) { - var label = e.target.getAttribute('data-range-key'); - this.chosenLabel = label; - if (label == this.locale.customRangeLabel) { - this.showCalendars(); - } else { - var dates = this.ranges[label]; - this.startDate = dates[0]; - this.endDate = dates[1]; - - if (!this.timePicker) { - this.startDate.startOf('day'); - this.endDate.endOf('day'); - } - - if (!this.alwaysShowCalendars) - this.hideCalendars(); - this.clickApply(); - } - }, - - clickPrev: function(e) { - var cal = $(e.target).parents('.calendar'); - if (cal.hasClass('left')) { - this.leftCalendar.month.subtract(1, 'month'); - if (this.linkedCalendars) - this.rightCalendar.month.subtract(1, 'month'); - } else { - this.rightCalendar.month.subtract(1, 'month'); - } - this.updateCalendars(); - }, - - clickNext: function(e) { - var cal = $(e.target).parents('.calendar'); - if (cal.hasClass('left')) { - this.leftCalendar.month.add(1, 'month'); - } else { - this.rightCalendar.month.add(1, 'month'); - if (this.linkedCalendars) - this.leftCalendar.month.add(1, 'month'); - } - this.updateCalendars(); - }, - - hoverDate: function(e) { - - //ignore mouse movements while an above-calendar text input has focus - //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - // return; - - //ignore dates that can't be selected - if (!$(e.target).hasClass('available')) return; - - //have the text inputs above calendars reflect the date being hovered over - var title = $(e.target).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); - var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; - - if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) { - this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format)); - } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) { - this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format)); - } - - //highlight the dates between the start date and the date being hovered as a potential end date - var leftCalendar = this.leftCalendar; - var rightCalendar = this.rightCalendar; - var startDate = this.startDate; - if (!this.endDate) { - this.container.find('.calendar tbody td').each(function(index, el) { - - //skip week numbers, only look at dates - if ($(el).hasClass('week')) return; - - var title = $(el).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(el).parents('.calendar'); - var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; - - if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) { - $(el).addClass('in-range'); - } else { - $(el).removeClass('in-range'); - } - - }); - } - - }, - - clickDate: function(e) { - - if (!$(e.target).hasClass('available')) return; - - var title = $(e.target).attr('data-title'); - var row = title.substr(1, 1); - var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); - var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; - - // - // this function needs to do a few things: - // * alternate between selecting a start and end date for the range, - // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date - // * if autoapply is enabled, and an end date was chosen, apply the selection - // * if single date picker mode, and time picker isn't enabled, apply the selection immediately - // * if one of the inputs above the calendars was focused, cancel that manual input - // - - if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start - if (this.timePicker) { - var hour = parseInt(this.container.find('.left .hourselect').val(), 10); - if (!this.timePicker24Hour) { - var ampm = this.container.find('.left .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - var minute = parseInt(this.container.find('.left .minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; - date = date.clone().hour(hour).minute(minute).second(second); - } - this.endDate = null; - this.setStartDate(date.clone()); - } else if (!this.endDate && date.isBefore(this.startDate)) { - //special case: clicking the same date for start/end, - //but the time of the end date is before the start date - this.setEndDate(this.startDate.clone()); - } else { // picking end - if (this.timePicker) { - var hour = parseInt(this.container.find('.right .hourselect').val(), 10); - if (!this.timePicker24Hour) { - var ampm = this.container.find('.right .ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - var minute = parseInt(this.container.find('.right .minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; - date = date.clone().hour(hour).minute(minute).second(second); - } - this.setEndDate(date.clone()); - if (this.autoApply) { - this.calculateChosenLabel(); - this.clickApply(); - } - } - - if (this.singleDatePicker) { - this.setEndDate(this.startDate); - if (!this.timePicker) - this.clickApply(); - } - - this.updateView(); - - //This is to cancel the blur event handler if the mouse was in one of the inputs - e.stopPropagation(); - - }, - - calculateChosenLabel: function () { - var customRange = true; - var i = 0; - for (var range in this.ranges) { - if (this.timePicker) { - var format = this.timePickerSeconds ? "YYYY-MM-DD hh:mm:ss" : "YYYY-MM-DD hh:mm"; - //ignore times when comparing dates if time picker seconds is not enabled - if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) { - customRange = false; - this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); - break; - } - } else { - //ignore times when comparing dates if time picker is not enabled - if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) { - customRange = false; - this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); - break; - } - } - i++; - } - if (customRange) { - if (this.showCustomRangeLabel) { - this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html(); - } else { - this.chosenLabel = null; - } - this.showCalendars(); - } - }, - - clickApply: function(e) { - this.hide(); - this.element.trigger('apply.daterangepicker', this); - }, - - clickCancel: function(e) { - this.startDate = this.oldStartDate; - this.endDate = this.oldEndDate; - this.hide(); - this.element.trigger('cancel.daterangepicker', this); - }, - - monthOrYearChanged: function(e) { - var isLeft = $(e.target).closest('.calendar').hasClass('left'), - leftOrRight = isLeft ? 'left' : 'right', - cal = this.container.find('.calendar.'+leftOrRight); - - // Month must be Number for new moment versions - var month = parseInt(cal.find('.monthselect').val(), 10); - var year = cal.find('.yearselect').val(); - - if (!isLeft) { - if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) { - month = this.startDate.month(); - year = this.startDate.year(); - } - } - - if (this.minDate) { - if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) { - month = this.minDate.month(); - year = this.minDate.year(); - } - } - - if (this.maxDate) { - if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) { - month = this.maxDate.month(); - year = this.maxDate.year(); - } - } - - if (isLeft) { - this.leftCalendar.month.month(month).year(year); - if (this.linkedCalendars) - this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month'); - } else { - this.rightCalendar.month.month(month).year(year); - if (this.linkedCalendars) - this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month'); - } - this.updateCalendars(); - }, - - timeChanged: function(e) { - - var cal = $(e.target).closest('.calendar'), - isLeft = cal.hasClass('left'); - - var hour = parseInt(cal.find('.hourselect').val(), 10); - var minute = parseInt(cal.find('.minuteselect').val(), 10); - var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; - - if (!this.timePicker24Hour) { - var ampm = cal.find('.ampmselect').val(); - if (ampm === 'PM' && hour < 12) - hour += 12; - if (ampm === 'AM' && hour === 12) - hour = 0; - } - - if (isLeft) { - var start = this.startDate.clone(); - start.hour(hour); - start.minute(minute); - start.second(second); - this.setStartDate(start); - if (this.singleDatePicker) { - this.endDate = this.startDate.clone(); - } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) { - this.setEndDate(start.clone()); - } - } else if (this.endDate) { - var end = this.endDate.clone(); - end.hour(hour); - end.minute(minute); - end.second(second); - this.setEndDate(end); - } - - //update the calendars so all clickable dates reflect the new time component - this.updateCalendars(); - - //update the form inputs above the calendars with the new time - this.updateFormInputs(); - - //re-render the time pickers because changing one selection can affect what's enabled in another - this.renderTimePicker('left'); - this.renderTimePicker('right'); - - }, - - formInputsChanged: function(e) { - var isRight = $(e.target).closest('.calendar').hasClass('right'); - var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format); - var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format); - - if (start.isValid() && end.isValid()) { - - if (isRight && end.isBefore(start)) - start = end.clone(); - - this.setStartDate(start); - this.setEndDate(end); - - if (isRight) { - this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format)); - } else { - this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format)); - } - - } - - this.updateView(); - }, - - formInputsFocused: function(e) { - - // Highlight the focused input - this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active'); - $(e.target).addClass('active'); - - // Set the state such that if the user goes back to using a mouse, - // the calendars are aware we're selecting the end of the range, not - // the start. This allows someone to edit the end of a date range without - // re-selecting the beginning, by clicking on the end date input then - // using the calendar. - var isRight = $(e.target).closest('.calendar').hasClass('right'); - if (isRight) { - this.endDate = null; - this.setStartDate(this.startDate.clone()); - this.updateView(); - } - - }, - - formInputsBlurred: function(e) { - - // this function has one purpose right now: if you tab from the first - // text input to the second in the UI, the endDate is nulled so that - // you can click another, but if you tab out without clicking anything - // or changing the input value, the old endDate should be retained - - if (!this.endDate) { - var val = this.container.find('input[name="daterangepicker_end"]').val(); - var end = moment(val, this.locale.format); - if (end.isValid()) { - this.setEndDate(end); - this.updateView(); - } - } - - }, - - formInputsKeydown: function(e) { - // This function ensures that if the 'enter' key was pressed in the input, then the calendars - // are updated with the startDate and endDate. - // This behaviour is automatic in Chrome/Firefox/Edge but not in IE 11 hence why this exists. - // Other browsers and versions of IE are untested and the behaviour is unknown. - if (e.keyCode === 13) { - // Prevent the calendar from being updated twice on Chrome/Firefox/Edge - e.preventDefault(); - this.formInputsChanged(e); - } - }, - - - elementChanged: function() { - if (!this.element.is('input')) return; - if (!this.element.val().length) return; - - var dateString = this.element.val().split(this.locale.separator), - start = null, - end = null; - - if (dateString.length === 2) { - start = moment(dateString[0], this.locale.format); - end = moment(dateString[1], this.locale.format); - } - - if (this.singleDatePicker || start === null || end === null) { - start = moment(this.element.val(), this.locale.format); - end = start; - } - - if (!start.isValid() || !end.isValid()) return; - - this.setStartDate(start); - this.setEndDate(end); - this.updateView(); - }, - - keydown: function(e) { - //hide on tab or enter - if ((e.keyCode === 9) || (e.keyCode === 13)) { - this.hide(); - } - - //hide on esc and prevent propagation - if (e.keyCode === 27) { - e.preventDefault(); - e.stopPropagation(); - - this.hide(); - } - }, - - updateElement: function() { - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); - } - }, - - remove: function() { - this.container.remove(); - this.element.off('.daterangepicker'); - this.element.removeData(); - } - - }; - - $.fn.daterangepicker = function(options, callback) { - var implementOptions = $.extend(true, {}, $.fn.daterangepicker.defaultOptions, options); - this.each(function() { - var el = $(this); - if (el.data('daterangepicker')) - el.data('daterangepicker').remove(); - el.data('daterangepicker', new DateRangePicker(el, implementOptions, callback)); - }); - return this; - }; - - return DateRangePicker; - -})); diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/daterangepicker.scss b/web/backend/resources/bower_components/bootstrap-daterangepicker/daterangepicker.scss deleted file mode 100644 index f61da9614..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/daterangepicker.scss +++ /dev/null @@ -1,611 +0,0 @@ -// -// A stylesheet for use with Bootstrap 3.x -// @author: Dan Grossman http://www.dangrossman.info/ -// @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved. -// @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php -// @website: https://www.improvely.com/ -// - -// -// VARIABLES -// - -// -// Settings - -// The class name to contain everything within. -$prefix-class: daterangepicker; -$arrow-size: 7px !default; - -// -// Colors -$daterangepicker-color: inherit !default; -$daterangepicker-bg-color: #fff !default; - -$daterangepicker-cell-color: $daterangepicker-color !default; -$daterangepicker-cell-border-color: transparent !default; -$daterangepicker-cell-bg-color: $daterangepicker-bg-color !default; - -$daterangepicker-cell-hover-color: $daterangepicker-color !default; -$daterangepicker-cell-hover-border-color: $daterangepicker-cell-border-color !default; -$daterangepicker-cell-hover-bg-color: #eee !default; - -$daterangepicker-in-range-color: #000 !default; -$daterangepicker-in-range-border-color: transparent !default; -$daterangepicker-in-range-bg-color: #ebf4f8 !default; - -$daterangepicker-active-color: #fff !default; -$daterangepicker-active-bg-color: #357ebd !default; -$daterangepicker-active-border-color: transparent !default; - -$daterangepicker-unselected-color: #999 !default; -$daterangepicker-unselected-border-color: transparent !default; -$daterangepicker-unselected-bg-color: #fff !default; - -// -// daterangepicker -$daterangepicker-width: 278px !default; -$daterangepicker-padding: 4px !default; -$daterangepicker-z-index: 3000 !default; - -$daterangepicker-border-size: 1px !default; -$daterangepicker-border-color: #ccc !default; -$daterangepicker-border-radius: 4px !default; - - -// -// Calendar -$daterangepicker-calendar-margin: $daterangepicker-padding !default; -$daterangepicker-calendar-bg-color: $daterangepicker-bg-color !default; - -$daterangepicker-calendar-border-size: 1px !default; -$daterangepicker-calendar-border-color: $daterangepicker-bg-color !default; -$daterangepicker-calendar-border-radius: $daterangepicker-border-radius !default; - -// -// Calendar Cells -$daterangepicker-cell-size: 20px !default; -$daterangepicker-cell-width: $daterangepicker-cell-size !default; -$daterangepicker-cell-height: $daterangepicker-cell-size !default; - -$daterangepicker-cell-border-radius: $daterangepicker-calendar-border-radius !default; -$daterangepicker-cell-border-size: 1px !default; - -// -// Dropdowns -$daterangepicker-dropdown-z-index: $daterangepicker-z-index + 1 !default; - -// -// Controls -$daterangepicker-control-height: 30px !default; -$daterangepicker-control-line-height: $daterangepicker-control-height !default; -$daterangepicker-control-color: #555 !default; - -$daterangepicker-control-border-size: 1px !default; -$daterangepicker-control-border-color: #ccc !default; -$daterangepicker-control-border-radius: 4px !default; - -$daterangepicker-control-active-border-size: 1px !default; -$daterangepicker-control-active-border-color: #08c !default; -$daterangepicker-control-active-border-radius: $daterangepicker-control-border-radius !default; - -$daterangepicker-control-disabled-color: #ccc !default; - -// -// Ranges -$daterangepicker-ranges-color: #08c !default; -$daterangepicker-ranges-bg-color: #f5f5f5 !default; - -$daterangepicker-ranges-border-size: 1px !default; -$daterangepicker-ranges-border-color: $daterangepicker-ranges-bg-color !default; -$daterangepicker-ranges-border-radius: $daterangepicker-border-radius !default; - -$daterangepicker-ranges-hover-color: #fff !default; -$daterangepicker-ranges-hover-bg-color: $daterangepicker-ranges-color !default; -$daterangepicker-ranges-hover-border-size: $daterangepicker-ranges-border-size !default; -$daterangepicker-ranges-hover-border-color: $daterangepicker-ranges-hover-bg-color !default; -$daterangepicker-ranges-hover-border-radius: $daterangepicker-border-radius !default; - -$daterangepicker-ranges-active-border-size: $daterangepicker-ranges-border-size !default; -$daterangepicker-ranges-active-border-color: $daterangepicker-ranges-bg-color !default; -$daterangepicker-ranges-active-border-radius: $daterangepicker-border-radius !default; - -// -// STYLESHEETS -// -.#{$prefix-class} { - position: absolute; - color: $daterangepicker-color; - background-color: $daterangepicker-bg-color; - border-radius: $daterangepicker-border-radius; - width: $daterangepicker-width; - padding: $daterangepicker-padding; - margin-top: $daterangepicker-border-size; - - // TODO: Should these be parameterized?? - top: 100px; - left: 20px; - - $arrow-prefix-size: $arrow-size; - $arrow-suffix-size: ($arrow-size - $daterangepicker-border-size); - - &:before, &:after { - position: absolute; - display: inline-block; - - border-bottom-color: rgba(0, 0, 0, 0.2); - content: ''; - } - - &:before { - top: -$arrow-prefix-size; - - border-right: $arrow-prefix-size solid transparent; - border-left: $arrow-prefix-size solid transparent; - border-bottom: $arrow-prefix-size solid $daterangepicker-border-color; - } - - &:after { - top: -$arrow-suffix-size; - - border-right: $arrow-suffix-size solid transparent; - border-bottom: $arrow-suffix-size solid $daterangepicker-bg-color; - border-left: $arrow-suffix-size solid transparent; - } - - &.opensleft { - &:before { - // TODO: Make this relative to prefix size. - right: $arrow-prefix-size + 2px; - } - - &:after { - // TODO: Make this relative to suffix size. - right: $arrow-suffix-size + 4px; - } - } - - &.openscenter { - &:before { - left: 0; - right: 0; - width: 0; - margin-left: auto; - margin-right: auto; - } - - &:after { - left: 0; - right: 0; - width: 0; - margin-left: auto; - margin-right: auto; - } - } - - &.opensright { - &:before { - // TODO: Make this relative to prefix size. - left: $arrow-prefix-size + 2px; - } - - &:after { - // TODO: Make this relative to suffix size. - left: $arrow-suffix-size + 4px; - } - } - - &.dropup { - margin-top: -5px; - - // NOTE: Note sure why these are special-cased. - &:before { - top: initial; - bottom: -$arrow-prefix-size; - border-bottom: initial; - border-top: $arrow-prefix-size solid $daterangepicker-border-color; - } - - &:after { - top: initial; - bottom:-$arrow-suffix-size; - border-bottom: initial; - border-top: $arrow-suffix-size solid $daterangepicker-bg-color; - } - } - - &.dropdown-menu { - max-width: none; - z-index: $daterangepicker-dropdown-z-index; - } - - &.single { - .ranges, .calendar { - float: none; - } - } - - /* Calendars */ - &.show-calendar { - .calendar { - display: block; - } - } - - .calendar { - display: none; - max-width: $daterangepicker-width - ($daterangepicker-calendar-margin * 2); - margin: $daterangepicker-calendar-margin; - - &.single { - .calendar-table { - border: none; - } - } - - th, td { - white-space: nowrap; - text-align: center; - - // TODO: Should this actually be hard-coded? - min-width: 32px; - } - } - - .calendar-table { - border: $daterangepicker-calendar-border-size solid $daterangepicker-calendar-border-color; - padding: $daterangepicker-calendar-margin; - border-radius: $daterangepicker-calendar-border-radius; - background-color: $daterangepicker-calendar-bg-color; - } - - table { - width: 100%; - margin: 0; - } - - td, th { - text-align: center; - width: $daterangepicker-cell-width; - height: $daterangepicker-cell-height; - border-radius: $daterangepicker-cell-border-radius; - border: $daterangepicker-cell-border-size solid $daterangepicker-cell-border-color; - white-space: nowrap; - cursor: pointer; - - &.available { - &:hover { - background-color: $daterangepicker-cell-hover-bg-color; - border-color: $daterangepicker-cell-hover-border-color; - color: $daterangepicker-cell-hover-color; - } - } - - &.week { - font-size: 80%; - color: #ccc; - } - } - - td { - &.off { - &, &.in-range, &.start-date, &.end-date { - background-color: $daterangepicker-unselected-bg-color; - border-color: $daterangepicker-unselected-border-color; - color: $daterangepicker-unselected-color; - } - } - - // - // Date Range - &.in-range { - background-color: $daterangepicker-in-range-bg-color; - border-color: $daterangepicker-in-range-border-color; - color: $daterangepicker-in-range-color; - - // TODO: Should this be static or should it be parameterized? - border-radius: 0; - } - - &.start-date { - border-radius: $daterangepicker-cell-border-radius 0 0 $daterangepicker-cell-border-radius; - } - - &.end-date { - border-radius: 0 $daterangepicker-cell-border-radius $daterangepicker-cell-border-radius 0; - } - - &.start-date.end-date { - border-radius: $daterangepicker-cell-border-radius; - } - - &.active { - &, &:hover { - background-color: $daterangepicker-active-bg-color; - border-color: $daterangepicker-active-border-color; - color: $daterangepicker-active-color; - } - } - } - - th { - &.month { - width: auto; - } - } - - // - // Disabled Controls - // - td, option { - &.disabled { - color: #999; - cursor: not-allowed; - text-decoration: line-through; - } - } - - select { - &.monthselect, &.yearselect { - font-size: 12px; - padding: 1px; - height: auto; - margin: 0; - cursor: default; - } - - &.monthselect { - margin-right: 2%; - width: 56%; - } - - &.yearselect { - width: 40%; - } - - &.hourselect, &.minuteselect, &.secondselect, &.ampmselect { - width: 50px; - margin-bottom: 0; - } - } - - // - // Text Input Controls (above calendar) - // - .input-mini { - border: $daterangepicker-control-border-size solid $daterangepicker-control-border-color; - border-radius: $daterangepicker-control-border-radius; - color: $daterangepicker-control-color; - height: $daterangepicker-control-line-height; - line-height: $daterangepicker-control-height; - display: block; - vertical-align: middle; - - // TODO: Should these all be static, too?? - margin: 0 0 5px 0; - padding: 0 6px 0 28px; - width: 100%; - - &.active { - border: $daterangepicker-control-active-border-size solid $daterangepicker-control-active-border-color; - border-radius: $daterangepicker-control-active-border-radius; - } - } - - .daterangepicker_input { - position: relative; - - i { - position: absolute; - - // NOTE: These appear to be eyeballed to me... - left: 8px; - top: 8px; - } - } - &.rtl { - .input-mini { - padding-right: 28px; - padding-left: 6px; - } - .daterangepicker_input i { - left: auto; - right: 8px; - } - } - - // - // Time Picker - // - .calendar-time { - text-align: center; - margin: 5px auto; - line-height: $daterangepicker-control-line-height; - position: relative; - padding-left: 28px; - - select { - &.disabled { - color: $daterangepicker-control-disabled-color; - cursor: not-allowed; - } - } - } -} - -// -// Predefined Ranges -// - -.ranges { - font-size: 11px; - float: none; - margin: 4px; - text-align: left; - - ul { - list-style: none; - margin: 0 auto; - padding: 0; - width: 100%; - } - - li { - font-size: 13px; - background-color: $daterangepicker-ranges-bg-color; - border: $daterangepicker-ranges-border-size solid $daterangepicker-ranges-border-color; - border-radius: $daterangepicker-ranges-border-radius; - color: $daterangepicker-ranges-color; - padding: 3px 12px; - margin-bottom: 8px; - cursor: pointer; - - &:hover { - background-color: $daterangepicker-ranges-hover-bg-color; - border: $daterangepicker-ranges-hover-border-size solid $daterangepicker-ranges-hover-border-color; - color: $daterangepicker-ranges-hover-color; - } - - &.active { - background-color: $daterangepicker-ranges-hover-bg-color; - border: $daterangepicker-ranges-hover-border-size solid $daterangepicker-ranges-hover-border-color; - color: $daterangepicker-ranges-hover-color; - } - } -} - -/* Larger Screen Styling */ -@media (min-width: 564px) { - .#{$prefix-class} { - width: auto; - - .ranges { - ul { - width: 160px; - } - } - - &.single { - .ranges { - ul { - width: 100%; - } - } - - .calendar.left { - clear: none; - } - - &.ltr { - .ranges, .calendar { - float:left; - } - } - &.rtl { - .ranges, .calendar { - float:right; - } - } - } - - &.ltr { - direction: ltr; - text-align: left; - .calendar{ - &.left { - clear: left; - margin-right: 0; - - .calendar-table { - border-right: none; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - } - - &.right { - margin-left: 0; - - .calendar-table { - border-left: none; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } - } - - .left .daterangepicker_input { - padding-right: 12px; - } - - .calendar.left .calendar-table { - padding-right: 12px; - } - - .ranges, .calendar { - float: left; - } - } - &.rtl { - direction: rtl; - text-align: right; - .calendar{ - &.left { - clear: right; - margin-left: 0; - - .calendar-table { - border-left: none; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } - - &.right { - margin-right: 0; - - .calendar-table { - border-right: none; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - } - } - - .left .daterangepicker_input { - padding-left: 12px; - } - - .calendar.left .calendar-table { - padding-left: 12px; - } - - .ranges, .calendar { - text-align: right; - float: right; - } - } - } -} - -@media (min-width: 730px) { - .#{$prefix-class} { - .ranges { - width: auto; - } - &.ltr { - .ranges { - float: left; - } - } - &.rtl { - .ranges { - float: right; - } - } - - .calendar.left { - clear: none !important; - } - } -} diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/demo.html b/web/backend/resources/bower_components/bootstrap-daterangepicker/demo.html deleted file mode 100644 index 7633ee426..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/demo.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - A date range picker for Bootstrap - - - - - - - - - - - - -
- -

Configuration Builder

- -
- -
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- -
- -
-
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
- -
- -
- -
-

Your Date Range Picker

- - -
- -
-

Configuration

- -
- -
-
- -
- -
- - - - - - - diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/drp.png b/web/backend/resources/bower_components/bootstrap-daterangepicker/drp.png deleted file mode 100644 index 9eee1fa33..000000000 Binary files a/web/backend/resources/bower_components/bootstrap-daterangepicker/drp.png and /dev/null differ diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/index.html b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/index.html deleted file mode 100644 index 533c94edf..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/index.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - A date range picker for Bootstrap - - - - - - -
- -

Configuration Builder

- -
- -
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
- -
- -
- -
-

Your Date Range Picker

- - -
- -
-

Configuration

- -
- -
-
- -
- -
- - - - - diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/main.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/main.js deleted file mode 100644 index 5c0f99a22..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/main.js +++ /dev/null @@ -1,141 +0,0 @@ -requirejs.config({ - "paths": { - "jquery": "https://code.jquery.com/jquery-1.11.3.min", - "moment": "../../moment", - "daterangepicker": "../../daterangepicker" - } -}); - -requirejs(['jquery', 'moment', 'daterangepicker'] , function ($, moment) { -$(document).ready(function() { - - $('#config-text').keyup(function() { - eval($(this).val()); - }); - - $('.configurator input, .configurator select').change(function() { - updateConfig(); - }); - - $('.demo i').click(function() { - $(this).parent().find('input').click(); - }); - - $('#startDate').daterangepicker({ - singleDatePicker: true, - startDate: moment().subtract(6, 'days') - }); - - $('#endDate').daterangepicker({ - singleDatePicker: true, - startDate: moment() - }); - - updateConfig(); - - function updateConfig() { - var options = {}; - - if ($('#singleDatePicker').is(':checked')) - options.singleDatePicker = true; - - if ($('#showDropdowns').is(':checked')) - options.showDropdowns = true; - - if ($('#showWeekNumbers').is(':checked')) - options.showWeekNumbers = true; - - if ($('#showISOWeekNumbers').is(':checked')) - options.showISOWeekNumbers = true; - - if ($('#timePicker').is(':checked')) - options.timePicker = true; - - if ($('#timePicker24Hour').is(':checked')) - options.timePicker24Hour = true; - - if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1) - options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10); - - if ($('#timePickerSeconds').is(':checked')) - options.timePickerSeconds = true; - - if ($('#autoApply').is(':checked')) - options.autoApply = true; - - if ($('#dateLimit').is(':checked')) - options.dateLimit = { days: 7 }; - - if ($('#ranges').is(':checked')) { - options.ranges = { - 'Today': [moment(), moment()], - 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], - 'Last 7 Days': [moment().subtract(6, 'days'), moment()], - 'Last 30 Days': [moment().subtract(29, 'days'), moment()], - 'This Month': [moment().startOf('month'), moment().endOf('month')], - 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] - }; - } - - if ($('#locale').is(':checked')) { - options.locale = { - format: 'MM/DD/YYYY HH:mm', - separator: ' - ', - applyLabel: 'Apply', - cancelLabel: 'Cancel', - fromLabel: 'From', - toLabel: 'To', - customRangeLabel: 'Custom', - daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'], - monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - firstDay: 1 - }; - } - - if (!$('#linkedCalendars').is(':checked')) - options.linkedCalendars = false; - - if (!$('#autoUpdateInput').is(':checked')) - options.autoUpdateInput = false; - - if ($('#alwaysShowCalendars').is(':checked')) - options.alwaysShowCalendars = true; - - if ($('#parentEl').val().length) - options.parentEl = $('#parentEl').val(); - - if ($('#startDate').val().length) - options.startDate = $('#startDate').val(); - - if ($('#endDate').val().length) - options.endDate = $('#endDate').val(); - - if ($('#minDate').val().length) - options.minDate = $('#minDate').val(); - - if ($('#maxDate').val().length) - options.maxDate = $('#maxDate').val(); - - if ($('#opens').val().length && $('#opens').val() != 'right') - options.opens = $('#opens').val(); - - if ($('#drops').val().length && $('#drops').val() != 'down') - options.drops = $('#drops').val(); - - if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm') - options.buttonClasses = $('#buttonClasses').val(); - - if ($('#applyClass').val().length && $('#applyClass').val() != 'btn-success') - options.applyClass = $('#applyClass').val(); - - if ($('#cancelClass').val().length && $('#cancelClass').val() != 'btn-default') - options.cancelClass = $('#cancelClass').val(); - - $('#config-text').val("$('#demo').daterangepicker(" + JSON.stringify(options, null, ' ') + ", function(start, end, label) {\n console.log(\"New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')\");\n});"); - - $('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); }); - - } - -}); -}); diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/require.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/require.js deleted file mode 100644 index 857eb5b70..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/amd/require.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - RequireJS 2.2.0 Copyright jQuery Foundation and other contributors. - Released under MIT license, http://github.com/requirejs/requirejs/LICENSE -*/ -var requirejs,require,define; -(function(ga){function ka(b,c,d,g){return g||""}function K(b){return"[object Function]"===Q.call(b)}function L(b){return"[object Array]"===Q.call(b)}function y(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(K(k)){if(this.events.error&&this.map.isDefine||g.onError!== -ha)try{h=l.execCb(c,k,b,h)}catch(d){a=d}else h=l.execCb(c,k,b,h);this.map.isDefine&&void 0===h&&((b=this.module)?h=b.exports:this.usingExports&&(h=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",A(this.error=a)}else h=k;this.exports=h;if(this.map.isDefine&&!this.ignore&&(v[c]=h,g.onResourceLoad)){var f=[];y(this.depMaps,function(a){f.push(a.normalizedMap||a)});g.onResourceLoad(l,this.map,f)}C(c); -this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}},callPlugin:function(){var a=this.map,b=a.id,d=q(a.prefix);this.depMaps.push(d);w(d,"defined",z(this,function(h){var k,f,d=e(fa,this.map.id),M=this.map.name,r=this.map.parentMap?this.map.parentMap.name:null,m=l.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(h.normalize&&(M=h.normalize(M,function(a){return c(a,r,!0)})|| -""),f=q(a.prefix+"!"+M,this.map.parentMap),w(f,"defined",z(this,function(a){this.map.normalizedMap=f;this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),h=e(t,f.id)){this.depMaps.push(f);if(this.events.error)h.on("error",z(this,function(a){this.emit("error",a)}));h.enable()}}else d?(this.map.url=l.nameToUrl(d),this.load()):(k=z(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),k.error=z(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];D(t,function(a){0=== -a.map.id.indexOf(b+"_unnormalized")&&C(a.map.id)});A(a)}),k.fromText=z(this,function(h,c){var d=a.name,f=q(d),M=S;c&&(h=c);M&&(S=!1);u(f);x(p.config,b)&&(p.config[d]=p.config[b]);try{g.exec(h)}catch(e){return A(F("fromtexteval","fromText eval for "+b+" failed: "+e,e,[b]))}M&&(S=!0);this.depMaps.push(f);l.completeLoad(d);m([d],k)}),h.load(a.name,m,k,p))}));l.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){Z[this.map.id]=this;this.enabling=this.enabled=!0;y(this.depMaps,z(this,function(a, -b){var c,h;if("string"===typeof a){a=q(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=e(R,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;w(a,"defined",z(this,function(a){this.undefed||(this.defineDep(b,a),this.check())}));this.errback?w(a,"error",z(this,this.errback)):this.events.error&&w(a,"error",z(this,function(a){this.emit("error",a)}))}c=a.id;h=t[c];x(R,c)||!h||h.enabled||l.enable(a,this)}));D(this.pluginMaps,z(this,function(a){var b=e(t,a.id); -b&&!b.enabled&&l.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){y(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};l={config:p,contextName:b,registry:t,defined:v,urlFetched:W,defQueue:G,defQueueMap:{},Module:da,makeModuleMap:q,nextTick:g.nextTick,onError:A,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");if("string"===typeof a.urlArgs){var b= -a.urlArgs;a.urlArgs=function(a,c){return(-1===c.indexOf("?")?"?":"&")+b}}var c=p.shim,h={paths:!0,bundles:!0,config:!0,map:!0};D(a,function(a,b){h[b]?(p[b]||(p[b]={}),Y(p[b],a,!0,!0)):p[b]=a});a.bundles&&D(a.bundles,function(a,b){y(a,function(a){a!==b&&(fa[a]=b)})});a.shim&&(D(a.shim,function(a,b){L(a)&&(a={deps:a});!a.exports&&!a.init||a.exportsFn||(a.exportsFn=l.makeShimExports(a));c[b]=a}),p.shim=c);a.packages&&y(a.packages,function(a){var b;a="string"===typeof a?{name:a}:a;b=a.name;a.location&& -(p.paths[b]=a.location);p.pkgs[b]=a.name+"/"+(a.main||"main").replace(na,"").replace(U,"")});D(t,function(a,b){a.inited||a.map.unnormalized||(a.map=q(b,null,!0))});(a.deps||a.callback)&&l.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ga,arguments));return b||a.exports&&ia(a.exports)}},makeRequire:function(a,n){function m(c,d,f){var e,r;n.enableBuildCallback&&d&&K(d)&&(d.__requireJsBuild=!0);if("string"===typeof c){if(K(d))return A(F("requireargs", -"Invalid require call"),f);if(a&&x(R,c))return R[c](t[a.id]);if(g.get)return g.get(l,c,a,m);e=q(c,a,!1,!0);e=e.id;return x(v,e)?v[e]:A(F("notloaded",'Module name "'+e+'" has not been loaded yet for context: '+b+(a?"":". Use require([])")))}P();l.nextTick(function(){P();r=u(q(null,a));r.skipMap=n.skipMap;r.init(c,d,f,{enabled:!0});H()});return m}n=n||{};Y(m,{isBrowser:E,toUrl:function(b){var d,f=b.lastIndexOf("."),g=b.split("/")[0];-1!==f&&("."!==g&&".."!==g||1e.attachEvent.toString().indexOf("[native code")||ca?(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)):(S=!0,e.attachEvent("onreadystatechange",b.onScriptLoad));e.src=d;if(m.onNodeCreated)m.onNodeCreated(e,m,c,d);P=e;H?C.insertBefore(e,H):C.appendChild(e);P=null;return e}if(ja)try{setTimeout(function(){}, -0),importScripts(d),b.completeLoad(c)}catch(q){b.onError(F("importscripts","importScripts failed for "+c+" at "+d,q,[c]))}};E&&!w.skipDataMain&&X(document.getElementsByTagName("script"),function(b){C||(C=b.parentNode);if(O=b.getAttribute("data-main"))return u=O,w.baseUrl||-1!==u.indexOf("!")||(I=u.split("/"),u=I.pop(),T=I.length?I.join("/")+"/":"./",w.baseUrl=T),u=u.replace(U,""),g.jsExtRegExp.test(u)&&(u=O),w.deps=w.deps?w.deps.concat(u):[u],!0});define=function(b,c,d){var e,g;"string"!==typeof b&& -(d=c,c=b,b=null);L(c)||(d=c,c=null);!c&&K(d)&&(c=[],d.length&&(d.toString().replace(qa,ka).replace(ra,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));S&&(e=P||pa())&&(b||(b=e.getAttribute("data-requiremodule")),g=J[e.getAttribute("data-requirecontext")]);g?(g.defQueue.push([b,c,d]),g.defQueueMap[b]=!0):V.push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(w)}})(this); diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/README.md b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/README.md deleted file mode 100644 index 123b093c5..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Browserify example - -Two steps need to be done for this to work - -In the project root - - npm install - -In this folder - - ../../node_modules/.bin/browserify main.js -o bundle.js diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/bundle.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/bundle.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/index.html b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/index.html deleted file mode 100644 index bdce82e8e..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/index.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - A date range picker for Bootstrap - - - - - - -
- -

Configuration Builder

- -
- -
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
- -
- -
- -
-

Your Date Range Picker

- - -
- -
-

Configuration

- -
- -
-
- -
- -
- - - - diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/main.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/main.js deleted file mode 100644 index 30e92d494..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/example/browserify/main.js +++ /dev/null @@ -1,135 +0,0 @@ -require('../../daterangepicker.js'); -var $ = require('jquery'), - moment = require('moment'); - -$(document).ready(function() { - - $('#config-text').keyup(function() { - eval($(this).val()); - }); - - $('.configurator input, .configurator select').change(function() { - updateConfig(); - }); - - $('.demo i').click(function() { - $(this).parent().find('input').click(); - }); - - $('#startDate').daterangepicker({ - singleDatePicker: true, - startDate: moment().subtract(6, 'days') - }); - - $('#endDate').daterangepicker({ - singleDatePicker: true, - startDate: moment() - }); - - updateConfig(); - - function updateConfig() { - var options = {}; - - if ($('#singleDatePicker').is(':checked')) - options.singleDatePicker = true; - - if ($('#showDropdowns').is(':checked')) - options.showDropdowns = true; - - if ($('#showWeekNumbers').is(':checked')) - options.showWeekNumbers = true; - - if ($('#showISOWeekNumbers').is(':checked')) - options.showISOWeekNumbers = true; - - if ($('#timePicker').is(':checked')) - options.timePicker = true; - - if ($('#timePicker24Hour').is(':checked')) - options.timePicker24Hour = true; - - if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1) - options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10); - - if ($('#timePickerSeconds').is(':checked')) - options.timePickerSeconds = true; - - if ($('#autoApply').is(':checked')) - options.autoApply = true; - - if ($('#dateLimit').is(':checked')) - options.dateLimit = { days: 7 }; - - if ($('#ranges').is(':checked')) { - options.ranges = { - 'Today': [moment(), moment()], - 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], - 'Last 7 Days': [moment().subtract(6, 'days'), moment()], - 'Last 30 Days': [moment().subtract(29, 'days'), moment()], - 'This Month': [moment().startOf('month'), moment().endOf('month')], - 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] - }; - } - - if ($('#locale').is(':checked')) { - options.locale = { - format: 'MM/DD/YYYY HH:mm', - separator: ' - ', - applyLabel: 'Apply', - cancelLabel: 'Cancel', - fromLabel: 'From', - toLabel: 'To', - customRangeLabel: 'Custom', - daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'], - monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - firstDay: 1 - }; - } - - if (!$('#linkedCalendars').is(':checked')) - options.linkedCalendars = false; - - if (!$('#autoUpdateInput').is(':checked')) - options.autoUpdateInput = false; - - if ($('#alwaysShowCalendars').is(':checked')) - options.alwaysShowCalendars = true; - - if ($('#parentEl').val().length) - options.parentEl = $('#parentEl').val(); - - if ($('#startDate').val().length) - options.startDate = $('#startDate').val(); - - if ($('#endDate').val().length) - options.endDate = $('#endDate').val(); - - if ($('#minDate').val().length) - options.minDate = $('#minDate').val(); - - if ($('#maxDate').val().length) - options.maxDate = $('#maxDate').val(); - - if ($('#opens').val().length && $('#opens').val() != 'right') - options.opens = $('#opens').val(); - - if ($('#drops').val().length && $('#drops').val() != 'down') - options.drops = $('#drops').val(); - - if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm') - options.buttonClasses = $('#buttonClasses').val(); - - if ($('#applyClass').val().length && $('#applyClass').val() != 'btn-success') - options.applyClass = $('#applyClass').val(); - - if ($('#cancelClass').val().length && $('#cancelClass').val() != 'btn-default') - options.cancelClass = $('#cancelClass').val(); - - $('#config-text').val("$('#demo').daterangepicker(" + JSON.stringify(options, null, ' ') + ", function(start, end, label) {\n console.log(\"New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')\");\n});"); - - $('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); }); - - } - -}); diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/package.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/package.js deleted file mode 100644 index 3dbe8b449..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/package.js +++ /dev/null @@ -1,18 +0,0 @@ -Package.describe({ - name: 'dangrossman:bootstrap-daterangepicker', - version: '2.1.27', - summary: 'Date range picker component for Bootstrap', - git: 'https://github.com/dangrossman/bootstrap-daterangepicker', - documentation: 'README.md' -}); - -Package.onUse(function(api) { - api.versionsFrom('METEOR@0.9.0.1'); - - api.use('twbs:bootstrap@3.3.4', ["client"], {weak: true}); - api.use('momentjs:moment@2.10.3', ["client"]); - api.use('jquery@1.11.3_2', ["client"]); - - api.addFiles('daterangepicker.js', ["client"]); - api.addFiles('daterangepicker.css', ["client"]); -}); diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/package.json b/web/backend/resources/bower_components/bootstrap-daterangepicker/package.json deleted file mode 100644 index 552bac647..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "bootstrap-daterangepicker", - "version": "2.1.27", - "description": "Date range picker component for Bootstrap", - "main": "daterangepicker.js", - "style": "daterangepicker.css", - "scripts": { - "scss": "node-sass daterangepicker.scss > daterangepicker.css", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/dangrossman/bootstrap-daterangepicker.git" - }, - "author": { - "name": "Dan Grossman", - "email": "dan@dangrossman.info", - "url": "http://www.dangrossman.info" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/dangrossman/bootstrap-daterangepicker/issues" - }, - "homepage": "https://github.com/dangrossman/bootstrap-daterangepicker", - "dependencies": { - "jquery": ">=1.10", - "moment": "^2.9.0" - }, - "devDependencies": { - "node-sass": "^3.4.2" - } -} diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/index.html b/web/backend/resources/bower_components/bootstrap-daterangepicker/website/index.html deleted file mode 100644 index fb9c66c15..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/index.html +++ /dev/null @@ -1,836 +0,0 @@ - - - - - Date Range Picker for Bootstrap - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- -

Date Range Picker

-

- A JavaScript component for choosing date ranges. -
- Designed to work with the Bootstrap CSS framework. -

- -
-
- - View on GitHub - -   - - Download ZIP - -

- - - - - - - -
-
-
-
- -
-
- - - -
- -
- -

- Originally built for reporting at Improvely, - the Date Range Picker can be attached to any webpage element to pop up two calendars - for selecting dates, times, or from predefined ranges like "Last 30 Days". -

- - - -
- -
- -

Usage

- -

- Date Range Picker relies on Bootstrap, jQuery and Moment.js. - - Include the required scripts and stylesheet in your page: -

- - - -

Then attach the picker to the element you want to trigger it:

- - - -
- -

- You can customize Date Range Picker with options, and - get notified when the user chooses new dates by providing a callback function. -

- - - -
- -
- -

Examples

- -
- -

Date Range Picker

- -

- The Date Range Picker is attached to a text input. It will use the current - value of the input to initialize, and update the input if new dates are chosen. -

- -
-
- -
-
-

Demo:

- -
-
- - - -
- -
- -

Date and Time

- -

- The Date Range Picker can also be used to select times. Hour, minute and (optional) - second dropdowns are added below the calendars. An option exists to set the increment - count of the minutes dropdown to e.g. offer only 15-minute or 30-minute increments. -

- -
-
- -
-
-

Demo:

- -
-
- - - -
- -
- -

Single Date Picker

- -

- The Date Range Picker can be turned into a single date picker widget with only - one calendar. In this example, dropdowns to select a month and year have also - been enabled at the top of the calendar to quickly jump to different months. -

- -
-
- -
-
-

Demo:

- -
-
- - - -
- -
- -

Predefined Ranges

- -

- This example shows the option to predefine date ranges that - the user can choose from a list. -

- -
-
- -
-
-

Demo:

-
-   - -
-
-
- - - -
- -
- -

Input Initially Empty

- -

- If you're using a date range as a filter, you may want to attach a picker to an - input but leave it empty by default. This example shows how to accomplish that - using the autoUpdateInput setting, and the apply and - cancel events. -

- -
-
- -
-
-

Demo:

- -
-
- - -
- - - -
- - - -
- -

Configuration Generator

- -
- -
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- -
-
- -
- -
- -
-

Your Date Range Picker

- - -
- -
-

Configuration

- -
- -
-
- -
- -
- -
- -

Options

- -
    -
  • - startDate (Date object, moment object or string) The start of the initially selected date range -
  • -
  • - endDate: (Date object, moment object or string) The end of the initially selected date range -
  • -
  • - minDate: (Date object, moment object or string) The earliest date a user may select -
  • -
  • - maxDate: (Date object, moment object or string) The latest date a user may select -
  • -
  • - dateLimit: (object) The maximum span between the selected start and end dates. Can have any property you can add to a moment object (i.e. days, months) -
  • -
  • - showDropdowns: (boolean) Show year and month select boxes above calendars to jump to a specific month and year -
  • -
  • - showWeekNumbers: (boolean) Show localized week numbers at the start of each week on the calendars -
  • -
  • - showISOWeekNumbers: (boolean) Show ISO week numbers at the start of each week on the calendars -
  • -
  • - timePicker: (boolean) Allow selection of dates with times, not just dates -
  • -
  • - timePickerIncrement: (number) Increment of the minutes selection list for times (i.e. 30 to allow only selection of times ending in 0 or 30) -
  • -
  • - timePicker24Hour: (boolean) Use 24-hour instead of 12-hour times, removing the AM/PM selection -
  • -
  • - timePickerSeconds: (boolean) Show seconds in the timePicker -
  • -
  • - ranges: (object) Set predefined date ranges the user can select from. Each key is the label for the range, and its value an array with two dates representing the bounds of the range -
  • -
  • - showCustomRangeLabel: (boolean) Displays an item labeled "Custom Range" at - the end of the list of predefined ranges, when the ranges option is used. - This option will be highlighted whenever the current date range selection does not match - one of the predefined ranges. Clicking it will display the calendars to select a new range. -
  • -
  • - alwaysShowCalendars: (boolean) Normally, if you use the ranges - option to specify pre-defined date ranges, calendars for choosing a custom date range are not shown until the user clicks "Custom Range". When this option is set to true, the calendars for choosing a custom date range are always shown instead. -
  • -
  • - opens: (string: 'left'/'right'/'center') Whether the picker appears aligned to the left, to the right, or centered under the HTML element it's attached to -
  • -
  • - drops: (string: 'down' or 'up') Whether the picker appears below (default) or above the HTML element it's attached to -
  • -
  • - buttonClasses: (array) CSS class names that will be added to all buttons in the picker -
  • -
  • - applyClass: (string) CSS class string that will be added to the apply button -
  • -
  • - cancelClass: (string) CSS class string that will be added to the cancel button -
  • -
  • - locale: (object) Allows you to provide localized strings for buttons and labels, customize the date format, and change the first day of week for the calendars. - Check off "locale (with example settings)" in the configuration generator to see how - to customize these options. -
  • -
  • - singleDatePicker: (boolean) Show only a single calendar to choose one date, instead of a range picker with two calendars; the start and end dates provided to your callback will be the same single date chosen -
  • -
  • - autoApply: (boolean) Hide the apply and cancel buttons, and automatically apply a new date range as soon as two dates or a predefined range is selected -
  • -
  • - linkedCalendars: (boolean) When enabled, the two calendars displayed will always be for two sequential months (i.e. January and February), and both will be advanced when clicking the left or right arrows above the calendars. When disabled, the two calendars can be individually advanced and display any month/year. -
  • -
  • - isInvalidDate: (function) A function that is passed each date in the two - calendars before they are displayed, and may return true or false to indicate whether - that date should be available for selection or not. -
  • -
  • - isCustomDate: (function) A function that is passed each date in the two - calendars before they are displayed, and may return a string or array of CSS class names - to apply to that date's calendar cell. -
  • -
  • - autoUpdateInput: (boolean) Indicates whether the date range picker should - automatically update the value of an <input> element it's attached to - at initialization and when the selected dates change. -
  • -
  • - parentEl: (string) jQuery selector of the parent element that the date range picker will be added to, if not provided this will be 'body' -
  • -
- -
- -
- -

Methods

- -

- You can programmatically update the startDate and endDate - in the picker using the setStartDate and setEndDate methods. - You can access the Date Range Picker object and its functions and properties through - data properties of the element you attached it to. -

- - - -
- -
    -
  • - setStartDate(Date/moment/string): Sets the date range picker's currently selected start date to the provided date -
  • -
  • - setEndDate(Date/moment/string): Sets the date range picker's currently selected end date to the provided date -
  • -
- -

Example usage:

- - - -
- -
- -

Events

- -

- Several events are triggered on the element you attach the picker to, which you can listen for. -

- -
    -
  • - show.daterangepicker: Triggered when the picker is shown -
  • -
  • - hide.daterangepicker: Triggered when the picker is hidden -
  • -
  • - showCalendar.daterangepicker: Triggered when the calendar(s) are shown -
  • -
  • - hideCalendar.daterangepicker: Triggered when the calendar(s) are hidden -
  • -
  • - apply.daterangepicker: Triggered when the apply button is clicked, - or when a predefined range is clicked -
  • -
  • - cancel.daterangepicker: Triggered when the cancel button is clicked -
  • -
- -

- Some applications need a "clear" instead of a "cancel" functionality, which can be achieved by changing the button label and watching for the cancel event: -

- - - -
- -

- While passing in a callback to the constructor is the easiest way to listen for changes in the selected date range, you can also do something every time the apply button is clicked even if the selection hasn't changed: -

- - - -
- -
- -

License

- -

The MIT License (MIT)

- -

Copyright (c) 2012-2017 Dan Grossman

- -

- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -

- -

- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -

- -

- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -

- -
- -
- -

Comments

- -
- - - -
- -
- -
-
- - - - - - - - - - - - - diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.css b/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.css deleted file mode 100644 index 74fbbdacb..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.css +++ /dev/null @@ -1,127 +0,0 @@ -body { - font-size: 15px; - line-height: 1.6em; - position: relative; - margin: 0; -} -.container { - width: 95%; - max-width: 1260px; -} -p, pre { - margin-bottom: 2em; -} -.main h2 { - font-weight: bold; - margin: 60px 0 20px 0; -} -.main h3 { - margin: 60px 0 20px 0; -} -.main h4 { - margin: 0 0 10px 0; - font-weight: bold; -} -ul.nobullets { - margin: 0; - padding: 0; - list-style-position: inside; -} -li { - padding-bottom: 1em; -} -#sidebar { - top: 20px; - width: 300px; -} -#sidebar ul { - margin-bottom: 5px; -} -#sidebar li { - margin-bottom: 0; - padding-bottom: 0; -} -#sidebar li ul { - display: none; -} -#sidebar li.active ul { - display: block; -} -#sidebar li li { - padding: 4px 0; -} -input[type="text"] { - padding: 6px; - width: 100%; - border-radius: 4px; -} -.navbar { - text-align: left; - margin: 0; - border: 0; -} -.navbar-inverse { - background: #ccc; -} -.navbar .container { - padding: 0 20px; -} -.navbar-nav li a:link, .navbar-nav li a:visited { - font-weight: bold; - color: #fff; - font-size: 16px; -} -.navbar-nav li { - background: #fff; -} -.navbar-nav li a:hover { - opacity: 0.8; -} -.navbar-nav li { - padding: 0; -} -.navbar-inverse .navbar-text { - margin: 18px 0 0 0; - color: #eee; -} -#footer { - background: #222; - margin-top: 80px; - padding: 30px; - color: #fff; - text-align: center; -} -#footer a:link, #footer a:visited { - color: #fff; - border-bottom: 1px dotted #fff; -} -#jumbo { - background: #f5f5f5 linear-gradient(to bottom,#eee 0,#f5f5f5 100%); - color: #000; - padding: 30px 0; - margin-bottom: 30px; -} -#jumbo .btn { - border-radius: 0; -} -#config .demo { position: relative; } -#config .demo i { position: absolute; bottom: 10px; right: 24px; top: auto; cursor: pointer; } - -#rightcol { - margin-left: 330px; -} - -#nav-spy { - float: left; - width: 300px; -} - -@media (max-width: 980px) { - #rightcol { - margin-left: 0; - } - #nav-spy { - float: none; - position: relative; - } -} diff --git a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.js b/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.js deleted file mode 100644 index 8ded2e9c4..000000000 --- a/web/backend/resources/bower_components/bootstrap-daterangepicker/website/website.js +++ /dev/null @@ -1,146 +0,0 @@ -$(document).ready(function() { - - $('#config-text').keyup(function() { - eval($(this).val()); - }); - - $('.configurator input, .configurator select').change(function() { - updateConfig(); - }); - - $('.demo i').click(function() { - $(this).parent().find('input').click(); - }); - - $('#startDate').daterangepicker({ - singleDatePicker: true, - startDate: moment().subtract(6, 'days') - }); - - $('#endDate').daterangepicker({ - singleDatePicker: true, - startDate: moment() - }); - - updateConfig(); - - function updateConfig() { - var options = {}; - - if ($('#singleDatePicker').is(':checked')) - options.singleDatePicker = true; - - if ($('#showDropdowns').is(':checked')) - options.showDropdowns = true; - - if ($('#showWeekNumbers').is(':checked')) - options.showWeekNumbers = true; - - if ($('#showISOWeekNumbers').is(':checked')) - options.showISOWeekNumbers = true; - - if ($('#timePicker').is(':checked')) - options.timePicker = true; - - if ($('#timePicker24Hour').is(':checked')) - options.timePicker24Hour = true; - - if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1) - options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10); - - if ($('#timePickerSeconds').is(':checked')) - options.timePickerSeconds = true; - - if ($('#autoApply').is(':checked')) - options.autoApply = true; - - if ($('#dateLimit').is(':checked')) - options.dateLimit = { days: 7 }; - - if ($('#ranges').is(':checked')) { - options.ranges = { - 'Today': [moment(), moment()], - 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], - 'Last 7 Days': [moment().subtract(6, 'days'), moment()], - 'Last 30 Days': [moment().subtract(29, 'days'), moment()], - 'This Month': [moment().startOf('month'), moment().endOf('month')], - 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] - }; - } - - if ($('#locale').is(':checked')) { - options.locale = { - format: 'MM/DD/YYYY', - separator: ' - ', - applyLabel: 'Apply', - cancelLabel: 'Cancel', - fromLabel: 'From', - toLabel: 'To', - customRangeLabel: 'Custom', - weekLabel: 'W', - daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'], - monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - firstDay: 1 - }; - } - - if (!$('#linkedCalendars').is(':checked')) - options.linkedCalendars = false; - - if (!$('#autoUpdateInput').is(':checked')) - options.autoUpdateInput = false; - - if (!$('#showCustomRangeLabel').is(':checked')) - options.showCustomRangeLabel = false; - - if ($('#alwaysShowCalendars').is(':checked')) - options.alwaysShowCalendars = true; - - if ($('#parentEl').val().length) - options.parentEl = $('#parentEl').val(); - - if ($('#startDate').val().length) - options.startDate = $('#startDate').val(); - - if ($('#endDate').val().length) - options.endDate = $('#endDate').val(); - - if ($('#minDate').val().length) - options.minDate = $('#minDate').val(); - - if ($('#maxDate').val().length) - options.maxDate = $('#maxDate').val(); - - if ($('#opens').val().length && $('#opens').val() != 'right') - options.opens = $('#opens').val(); - - if ($('#drops').val().length && $('#drops').val() != 'down') - options.drops = $('#drops').val(); - - if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm') - options.buttonClasses = $('#buttonClasses').val(); - - if ($('#applyClass').val().length && $('#applyClass').val() != 'btn-success') - options.applyClass = $('#applyClass').val(); - - if ($('#cancelClass').val().length && $('#cancelClass').val() != 'btn-default') - options.cancelClass = $('#cancelClass').val(); - - $('#config-text').val("$('#demo').daterangepicker(" + JSON.stringify(options, null, ' ') + ", function(start, end, label) {\n console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')');\n});"); - - $('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); }); - - } - - if ($(window).width() > 980) { - $('#sidebar').affix({ - offset: { - top: 300, - bottom: function () { - return (this.bottom = $('.footer').outerHeight(true)) - } - } - }); - } - $('body').scrollspy({ target: '#nav-spy', offset: 20 }); -}); diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/.bower.json b/web/backend/resources/bower_components/bootstrap-timepicker/.bower.json deleted file mode 100644 index dfe8dd6ce..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/.bower.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "bootstrap-timepicker", - "description": "A timepicker component for Twitter Bootstrap", - "version": "0.5.2", - "main": "js/bootstrap-timepicker.js", - "license": "MIT", - "ignore": [ - "**/.*", - "_layouts", - "node_modules", - "_config.yml", - "assets", - "spec", - "index.html", - "Gruntfile.js", - "package.json", - "composer.json" - ], - "repository": { - "type": "git", - "url": "https://github.com/jdewit/bootstrap-timepicker" - }, - "dependencies": { - "bootstrap": "^3.0", - "jquery": "^2.0" - }, - "devDependencies": { - "autotype": "https://raw.github.com/mmonteleone/jquery.autotype/master/jquery.autotype.js" - }, - "keywords": [ - "widget", - "timepicker", - "time" - ], - "homepage": "https://github.com/jdewit/bootstrap-timepicker", - "_release": "0.5.2", - "_resolution": { - "type": "version", - "tag": "v0.5.2", - "commit": "5ac75ccbe2f53a7357fd97ca9afdebe014b4c027" - }, - "_source": "https://github.com/jdewit/bootstrap-timepicker.git", - "_target": "^0.5.2", - "_originalSource": "bootstrap-timepicker" -} \ No newline at end of file diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/CHANGELOG.md b/web/backend/resources/bower_components/bootstrap-timepicker/CHANGELOG.md deleted file mode 100644 index 809b619de..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/CHANGELOG.md +++ /dev/null @@ -1,56 +0,0 @@ -# Changelog - -All notable changes will be documented in this file. This project -sort of conforms to Semantic Versioning. Since we're still pre-1.0, -it's like the Wild West up in here! - -## Unreleased -### Added (not started) -- Still planning out how to include i18n data and functionality. - -### Deprecated (not started) -- Incorrect usage of the word "meridian" will be deprecated. It should - be "meridiem". -- `showWidgetOnAddonClick`'s current behavior is not intuitive. Clicking - the input addon should _toggle_ the widget instead of showing it. - -## 0.5.2 - 2016-01-02 -### Added -- Tabbing out of the timepicker widget will now close it. -- You can specify your own icon classes. See docs for the option. - -### Changed -- Cleaned up `package.json` and `bower.json` files. The npm/bower package - should be cleaner now. -- `timepicker.less` now lives in the `css/` directory of the package. -- bootstrap-timepicker now uses the latest minor releases for jQuery 2 and - Bootstrap 3 - -### Fixed -- Fixed bad interaction between `setTime("12:00 AM")` and `showMeridian` -- Various documentation issues were fixed. - -## 0.5.1 - 2015-08-06 -### Changed -- Critical fix (#279) for bootstrap initialization. If you happened to - list your timepicker's classes in an order other than "input-group - bootstrap-timepicker", you'd be out of luck. Now we use jQuery's - `hasClass` method correctly. Yay! - -## 0.5 - 2015-07-31 -### Changed -- Bootstrap 3 support. No more Bootstrap 2 support. -- setTime sets time better -- more tests, and they exercise Bootstrap 3 support! -- snapToStep is a new option, off by default, which snaps times to the - nearest step or overflows to 0 if it would otherwise snap to 60 or - more. -- explicitMode is a new option, off by default, which lets you leave - out colons when typing times. -- shift+tab now correctly moves the cursor to the previously - highlighted unit, and blurs the timepicker when expected. -- We have cut out significant amounts of old cruft from the - repository. -- Minified/Uglified code is no longer kept in the repo. Please - download a release tarball or zip file to get the compiled and - minified CSS and Javascript files. diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/LICENSE b/web/backend/resources/bower_components/bootstrap-timepicker/LICENSE deleted file mode 100644 index a43ecdcfc..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT license - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/README.md b/web/backend/resources/bower_components/bootstrap-timepicker/README.md deleted file mode 100644 index 28ee1213b..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/README.md +++ /dev/null @@ -1,73 +0,0 @@ -Timepicker for Twitter Bootstrap -======= -[![Build Status](https://travis-ci.org/jdewit/bootstrap-timepicker.svg?branch=gh-pages)](https://travis-ci.org/jdewit/bootstrap-timepicker) - -A simple timepicker component for Twitter Bootstrap. - -Status -====== -Please take a look at the `CHANGELOG.md` and the issues tab for issues we're -working on and their relative priorities. - -Installation -============ - -This project is registered as a Bower package, -and can be installed with the following command: - -```bash -bower install bootstrap-timepicker -``` - -You can also download our latest release (and any previous release) -here. - -Demos & Documentation -===================== - -View demos & documentation. - -Support -======= - -If you make money using this timepicker, please consider -supporting its development. - -Click here to support bootstrap-timepicker! - -Contributing -============ - -1. Install NodeJS and Node Package Manager. - -2. Install packages - -```bash -npm install -``` - -3. Use Bower to get the dev dependencies. - -```bash -bower install -``` - -4. Use Grunt to run tests, compress assets, etc. - -```bash -grunt test // run jshint and jasmine tests -grunt watch // run jsHint and Jasmine tests whenever a file is changed -grunt compile // minify the js and css files -``` - -- Please make it easy on me by covering any new features or issues - with Jasmine tests. -- If your changes need documentation, please take the time to update the docs. - -Acknowledgements -================ - -Thanks to everyone who have given feedback and submitted pull requests. A -list of all the contributors can be found here. - -Special thanks to @eternicode and his Twitter Datepicker for inspiration. diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/bower.json b/web/backend/resources/bower_components/bootstrap-timepicker/bower.json deleted file mode 100644 index abb6afe8f..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/bower.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "bootstrap-timepicker", - "description": "A timepicker component for Twitter Bootstrap", - "version": "0.5.2", - "main": "js/bootstrap-timepicker.js", - "license": "MIT", - "ignore": [ - "**/.*", - "_layouts", - "node_modules", - "_config.yml", - "assets", - "spec", - "index.html", - "Gruntfile.js", - "package.json", - "composer.json" - ], - "repository": { - "type": "git", - "url": "https://github.com/jdewit/bootstrap-timepicker" - }, - "dependencies": { - "bootstrap": "^3.0", - "jquery": "^2.0" - }, - "devDependencies": { - "autotype": "https://raw.github.com/mmonteleone/jquery.autotype/master/jquery.autotype.js" - }, - "keywords": [ - "widget", - "timepicker", - "time" - ] -} diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/css/timepicker.less b/web/backend/resources/bower_components/bootstrap-timepicker/css/timepicker.less deleted file mode 100644 index a2e813f9f..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/css/timepicker.less +++ /dev/null @@ -1,172 +0,0 @@ -/*! - * Timepicker Component for Twitter Bootstrap - * - * Copyright 2013 Joris de Wit - * - * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -.bootstrap-timepicker { - position: relative; - - &.pull-right { - .bootstrap-timepicker-widget { - &.dropdown-menu { - left: auto; - right: 0; - - &:before { - left: auto; - right: 12px; - } - &:after { - left: auto; - right: 13px; - } - } - } - } - - .input-group-addon { - cursor: pointer; - i { - display: inline-block; - width: 16px; - height: 16px; - } - } -} -.bootstrap-timepicker-widget { - &.dropdown-menu { - padding: 4px; - &.open { - display: inline-block; - } - &:before { - border-bottom: 7px solid rgba(0, 0, 0, 0.2); - border-left: 7px solid transparent; - border-right: 7px solid transparent; - content: ""; - display: inline-block; - position: absolute; - } - &:after { - border-bottom: 6px solid #FFFFFF; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - content: ""; - display: inline-block; - position: absolute; - } - } - &.timepicker-orient-left { - &:before { - left: 6px; - } - &:after { - left: 7px; - } - } - &.timepicker-orient-right { - &:before { - right: 6px; - } - &:after { - right: 7px; - } - } - &.timepicker-orient-top { - &:before { - top: -7px; - } - &:after { - top: -6px; - } - } - &.timepicker-orient-bottom { - &:before { - bottom: -7px; - border-bottom: 0; - border-top: 7px solid #999; - } - &:after { - bottom: -6px; - border-bottom: 0; - border-top: 6px solid #ffffff; - } - } - a.btn, input { - border-radius: 4px; - } - - table { - width: 100%; - margin: 0; - - td { - text-align: center; - height: 30px; - margin: 0; - padding: 2px; - - &:not(.separator) { - min-width: 30px; - } - - span { - width: 100%; - } - a { - border: 1px transparent solid; - width: 100%; - display: inline-block; - margin: 0; - padding: 8px 0; - outline: 0; - color: #333; - - &:hover { - text-decoration: none; - background-color: #eee; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - border-color: #ddd; - } - - i { - margin-top: 2px; - font-size: 18px; - } - } - input { - width: 25px; - margin: 0; - text-align: center; - } - } - } -} - -.bootstrap-timepicker-widget .modal-content { - padding: 4px; -} - -@media (min-width: 767px) { - .bootstrap-timepicker-widget.modal { - width: 200px; - margin-left: -100px; - } -} - -@media (max-width: 767px) { - .bootstrap-timepicker { - width: 100%; - - .dropdown-menu { - width: 100%; - } - } -} diff --git a/web/backend/resources/bower_components/bootstrap-timepicker/js/bootstrap-timepicker.js b/web/backend/resources/bower_components/bootstrap-timepicker/js/bootstrap-timepicker.js deleted file mode 100644 index bbb923c5e..000000000 --- a/web/backend/resources/bower_components/bootstrap-timepicker/js/bootstrap-timepicker.js +++ /dev/null @@ -1,1177 +0,0 @@ -/*! - * Timepicker Component for Twitter Bootstrap - * - * Copyright 2013 Joris de Wit and bootstrap-timepicker contributors - * - * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -(function($, window, document) { - 'use strict'; - - // TIMEPICKER PUBLIC CLASS DEFINITION - var Timepicker = function(element, options) { - this.widget = ''; - this.$element = $(element); - this.defaultTime = options.defaultTime; - this.disableFocus = options.disableFocus; - this.disableMousewheel = options.disableMousewheel; - this.isOpen = options.isOpen; - this.minuteStep = options.minuteStep; - this.modalBackdrop = options.modalBackdrop; - this.orientation = options.orientation; - this.secondStep = options.secondStep; - this.snapToStep = options.snapToStep; - this.showInputs = options.showInputs; - this.showMeridian = options.showMeridian; - this.showSeconds = options.showSeconds; - this.template = options.template; - this.appendWidgetTo = options.appendWidgetTo; - this.showWidgetOnAddonClick = options.showWidgetOnAddonClick; - this.icons = options.icons; - this.maxHours = options.maxHours; - this.explicitMode = options.explicitMode; // If true 123 = 1:23, 12345 = 1:23:45, else invalid. - - this.handleDocumentClick = function (e) { - var self = e.data.scope; - // This condition was inspired by bootstrap-datepicker. - // The element the timepicker is invoked on is the input but it has a sibling for addon/button. - if (!(self.$element.parent().find(e.target).length || - self.$widget.is(e.target) || - self.$widget.find(e.target).length)) { - self.hideWidget(); - } - }; - - this._init(); - }; - - Timepicker.prototype = { - - constructor: Timepicker, - _init: function() { - var self = this; - - if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-group') && this.$element.parent().hasClass('bootstrap-timepicker'))) { - this.$element.parent('.input-group.bootstrap-timepicker').find('.input-group-addon').on({ - 'click.timepicker': $.proxy(this.showWidget, this) - }); - this.$element.on({ - 'focus.timepicker': $.proxy(this.highlightUnit, this), - 'click.timepicker': $.proxy(this.highlightUnit, this), - 'keydown.timepicker': $.proxy(this.elementKeydown, this), - 'blur.timepicker': $.proxy(this.blurElement, this), - 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) - }); - } else { - if (this.template) { - this.$element.on({ - 'focus.timepicker': $.proxy(this.showWidget, this), - 'click.timepicker': $.proxy(this.showWidget, this), - 'blur.timepicker': $.proxy(this.blurElement, this), - 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) - }); - } else { - this.$element.on({ - 'focus.timepicker': $.proxy(this.highlightUnit, this), - 'click.timepicker': $.proxy(this.highlightUnit, this), - 'keydown.timepicker': $.proxy(this.elementKeydown, this), - 'blur.timepicker': $.proxy(this.blurElement, this), - 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this) - }); - } - } - - if (this.template !== false) { - this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this)); - } else { - this.$widget = false; - } - - if (this.showInputs && this.$widget !== false) { - this.$widget.find('input').each(function() { - $(this).on({ - 'click.timepicker': function() { $(this).select(); }, - 'keydown.timepicker': $.proxy(self.widgetKeydown, self), - 'keyup.timepicker': $.proxy(self.widgetKeyup, self) - }); - }); - } - - this.setDefaultTime(this.defaultTime); - }, - - blurElement: function() { - this.highlightedUnit = null; - this.updateFromElementVal(); - }, - - clear: function() { - this.hour = ''; - this.minute = ''; - this.second = ''; - this.meridian = ''; - - this.$element.val(''); - }, - - decrementHour: function() { - if (this.showMeridian) { - if (this.hour === 1) { - this.hour = 12; - } else if (this.hour === 12) { - this.hour--; - - return this.toggleMeridian(); - } else if (this.hour === 0) { - this.hour = 11; - - return this.toggleMeridian(); - } else { - this.hour--; - } - } else { - if (this.hour <= 0) { - this.hour = this.maxHours - 1; - } else { - this.hour--; - } - } - }, - - decrementMinute: function(step) { - var newVal; - - if (step) { - newVal = this.minute - step; - } else { - newVal = this.minute - this.minuteStep; - } - - if (newVal < 0) { - this.decrementHour(); - this.minute = newVal + 60; - } else { - this.minute = newVal; - } - }, - - decrementSecond: function() { - var newVal = this.second - this.secondStep; - - if (newVal < 0) { - this.decrementMinute(true); - this.second = newVal + 60; - } else { - this.second = newVal; - } - }, - - elementKeydown: function(e) { - switch (e.which) { - case 9: //tab - if (e.shiftKey) { - if (this.highlightedUnit === 'hour') { - this.hideWidget(); - break; - } - this.highlightPrevUnit(); - } else if ((this.showMeridian && this.highlightedUnit === 'meridian') || (this.showSeconds && this.highlightedUnit === 'second') || (!this.showMeridian && !this.showSeconds && this.highlightedUnit ==='minute')) { - this.hideWidget(); - break; - } else { - this.highlightNextUnit(); - } - e.preventDefault(); - this.updateFromElementVal(); - break; - case 27: // escape - this.updateFromElementVal(); - break; - case 37: // left arrow - e.preventDefault(); - this.highlightPrevUnit(); - this.updateFromElementVal(); - break; - case 38: // up arrow - e.preventDefault(); - switch (this.highlightedUnit) { - case 'hour': - this.incrementHour(); - this.highlightHour(); - break; - case 'minute': - this.incrementMinute(); - this.highlightMinute(); - break; - case 'second': - this.incrementSecond(); - this.highlightSecond(); - break; - case 'meridian': - this.toggleMeridian(); - this.highlightMeridian(); - break; - } - this.update(); - break; - case 39: // right arrow - e.preventDefault(); - this.highlightNextUnit(); - this.updateFromElementVal(); - break; - case 40: // down arrow - e.preventDefault(); - switch (this.highlightedUnit) { - case 'hour': - this.decrementHour(); - this.highlightHour(); - break; - case 'minute': - this.decrementMinute(); - this.highlightMinute(); - break; - case 'second': - this.decrementSecond(); - this.highlightSecond(); - break; - case 'meridian': - this.toggleMeridian(); - this.highlightMeridian(); - break; - } - - this.update(); - break; - } - }, - - getCursorPosition: function() { - var input = this.$element.get(0); - - if ('selectionStart' in input) {// Standard-compliant browsers - - return input.selectionStart; - } else if (document.selection) {// IE fix - input.focus(); - var sel = document.selection.createRange(), - selLen = document.selection.createRange().text.length; - - sel.moveStart('character', - input.value.length); - - return sel.text.length - selLen; - } - }, - - getTemplate: function() { - var template, - hourTemplate, - minuteTemplate, - secondTemplate, - meridianTemplate, - templateContent; - - if (this.showInputs) { - hourTemplate = ''; - minuteTemplate = ''; - secondTemplate = ''; - meridianTemplate = ''; - } else { - hourTemplate = ''; - minuteTemplate = ''; - secondTemplate = ''; - meridianTemplate = ''; - } - - templateContent = ''+ - ''+ - ''+ - ''+ - ''+ - (this.showSeconds ? - ''+ - '' - : '') + - (this.showMeridian ? - ''+ - '' - : '') + - ''+ - ''+ - ' '+ - ''+ - ' '+ - (this.showSeconds ? - ''+ - '' - : '') + - (this.showMeridian ? - ''+ - '' - : '') + - ''+ - ''+ - ''+ - ''+ - ''+ - (this.showSeconds ? - ''+ - '' - : '') + - (this.showMeridian ? - ''+ - '' - : '') + - ''+ - '
   
'+ hourTemplate +':'+ minuteTemplate +':'+ secondTemplate +' '+ meridianTemplate +'
  
'; - - switch(this.template) { - case 'modal': - template = ''; - break; - case 'dropdown': - template = ''; - break; - } - - return template; - }, - - getTime: function() { - if (this.hour === '') { - return ''; - } - - return this.hour + ':' + (this.minute.toString().length === 1 ? '0' + this.minute : this.minute) + (this.showSeconds ? ':' + (this.second.toString().length === 1 ? '0' + this.second : this.second) : '') + (this.showMeridian ? ' ' + this.meridian : ''); - }, - - hideWidget: function() { - if (this.isOpen === false) { - return; - } - - this.$element.trigger({ - 'type': 'hide.timepicker', - 'time': { - 'value': this.getTime(), - 'hours': this.hour, - 'minutes': this.minute, - 'seconds': this.second, - 'meridian': this.meridian - } - }); - - if (this.template === 'modal' && this.$widget.modal) { - this.$widget.modal('hide'); - } else { - this.$widget.removeClass('open'); - } - - $(document).off('mousedown.timepicker, touchend.timepicker', this.handleDocumentClick); - - this.isOpen = false; - // show/hide approach taken by datepicker - this.$widget.detach(); - }, - - highlightUnit: function() { - this.position = this.getCursorPosition(); - if (this.position >= 0 && this.position <= 2) { - this.highlightHour(); - } else if (this.position >= 3 && this.position <= 5) { - this.highlightMinute(); - } else if (this.position >= 6 && this.position <= 8) { - if (this.showSeconds) { - this.highlightSecond(); - } else { - this.highlightMeridian(); - } - } else if (this.position >= 9 && this.position <= 11) { - this.highlightMeridian(); - } - }, - - highlightNextUnit: function() { - switch (this.highlightedUnit) { - case 'hour': - this.highlightMinute(); - break; - case 'minute': - if (this.showSeconds) { - this.highlightSecond(); - } else if (this.showMeridian){ - this.highlightMeridian(); - } else { - this.highlightHour(); - } - break; - case 'second': - if (this.showMeridian) { - this.highlightMeridian(); - } else { - this.highlightHour(); - } - break; - case 'meridian': - this.highlightHour(); - break; - } - }, - - highlightPrevUnit: function() { - switch (this.highlightedUnit) { - case 'hour': - if(this.showMeridian){ - this.highlightMeridian(); - } else if (this.showSeconds) { - this.highlightSecond(); - } else { - this.highlightMinute(); - } - break; - case 'minute': - this.highlightHour(); - break; - case 'second': - this.highlightMinute(); - break; - case 'meridian': - if (this.showSeconds) { - this.highlightSecond(); - } else { - this.highlightMinute(); - } - break; - } - }, - - highlightHour: function() { - var $element = this.$element.get(0), - self = this; - - this.highlightedUnit = 'hour'; - - if ($element.setSelectionRange) { - setTimeout(function() { - if (self.hour < 10) { - $element.setSelectionRange(0,1); - } else { - $element.setSelectionRange(0,2); - } - }, 0); - } - }, - - highlightMinute: function() { - var $element = this.$element.get(0), - self = this; - - this.highlightedUnit = 'minute'; - - if ($element.setSelectionRange) { - setTimeout(function() { - if (self.hour < 10) { - $element.setSelectionRange(2,4); - } else { - $element.setSelectionRange(3,5); - } - }, 0); - } - }, - - highlightSecond: function() { - var $element = this.$element.get(0), - self = this; - - this.highlightedUnit = 'second'; - - if ($element.setSelectionRange) { - setTimeout(function() { - if (self.hour < 10) { - $element.setSelectionRange(5,7); - } else { - $element.setSelectionRange(6,8); - } - }, 0); - } - }, - - highlightMeridian: function() { - var $element = this.$element.get(0), - self = this; - - this.highlightedUnit = 'meridian'; - - if ($element.setSelectionRange) { - if (this.showSeconds) { - setTimeout(function() { - if (self.hour < 10) { - $element.setSelectionRange(8,10); - } else { - $element.setSelectionRange(9,11); - } - }, 0); - } else { - setTimeout(function() { - if (self.hour < 10) { - $element.setSelectionRange(5,7); - } else { - $element.setSelectionRange(6,8); - } - }, 0); - } - } - }, - - incrementHour: function() { - if (this.showMeridian) { - if (this.hour === 11) { - this.hour++; - return this.toggleMeridian(); - } else if (this.hour === 12) { - this.hour = 0; - } - } - if (this.hour === this.maxHours - 1) { - this.hour = 0; - - return; - } - this.hour++; - }, - - incrementMinute: function(step) { - var newVal; - - if (step) { - newVal = this.minute + step; - } else { - newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep); - } - - if (newVal > 59) { - this.incrementHour(); - this.minute = newVal - 60; - } else { - this.minute = newVal; - } - }, - - incrementSecond: function() { - var newVal = this.second + this.secondStep - (this.second % this.secondStep); - - if (newVal > 59) { - this.incrementMinute(true); - this.second = newVal - 60; - } else { - this.second = newVal; - } - }, - - mousewheel: function(e) { - if (this.disableMousewheel) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail, - scrollTo = null; - - if (e.type === 'mousewheel') { - scrollTo = (e.originalEvent.wheelDelta * -1); - } - else if (e.type === 'DOMMouseScroll') { - scrollTo = 40 * e.originalEvent.detail; - } - - if (scrollTo) { - e.preventDefault(); - $(this).scrollTop(scrollTo + $(this).scrollTop()); - } - - switch (this.highlightedUnit) { - case 'minute': - if (delta > 0) { - this.incrementMinute(); - } else { - this.decrementMinute(); - } - this.highlightMinute(); - break; - case 'second': - if (delta > 0) { - this.incrementSecond(); - } else { - this.decrementSecond(); - } - this.highlightSecond(); - break; - case 'meridian': - this.toggleMeridian(); - this.highlightMeridian(); - break; - default: - if (delta > 0) { - this.incrementHour(); - } else { - this.decrementHour(); - } - this.highlightHour(); - break; - } - - return false; - }, - - /** - * Given a segment value like 43, will round and snap the segment - * to the nearest "step", like 45 if step is 15. Segment will - * "overflow" to 0 if it's larger than 59 or would otherwise - * round up to 60. - */ - changeToNearestStep: function (segment, step) { - if (segment % step === 0) { - return segment; - } - if (Math.round((segment % step) / step)) { - return (segment + (step - segment % step)) % 60; - } else { - return segment - segment % step; - } - }, - - // This method was adapted from bootstrap-datepicker. - place : function() { - if (this.isInline) { - return; - } - var widgetWidth = this.$widget.outerWidth(), widgetHeight = this.$widget.outerHeight(), visualPadding = 10, windowWidth = - $(window).width(), windowHeight = $(window).height(), scrollTop = $(window).scrollTop(); - - var zIndex = parseInt(this.$element.parents().filter(function() { return $(this).css('z-index') !== 'auto'; }).first().css('z-index'), 10) + 10; - var offset = this.component ? this.component.parent().offset() : this.$element.offset(); - var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false); - var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false); - var left = offset.left, top = offset.top; - - this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left'); - - if (this.orientation.x !== 'auto') { - this.$widget.addClass('timepicker-orient-' + this.orientation.x); - if (this.orientation.x === 'right') { - left -= widgetWidth - width; - } - } else{ - // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways - // Default to left - this.$widget.addClass('timepicker-orient-left'); - if (offset.left < 0) { - left -= offset.left - visualPadding; - } else if (offset.left + widgetWidth > windowWidth) { - left = windowWidth - widgetWidth - visualPadding; - } - } - // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget - var yorient = this.orientation.y, topOverflow, bottomOverflow; - if (yorient === 'auto') { - topOverflow = -scrollTop + offset.top - widgetHeight; - bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight); - if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) { - yorient = 'top'; - } else { - yorient = 'bottom'; - } - } - this.$widget.addClass('timepicker-orient-' + yorient); - if (yorient === 'top'){ - top += height; - } else{ - top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10); - } - - this.$widget.css({ - top : top, - left : left, - zIndex : zIndex - }); - }, - - remove: function() { - $('document').off('.timepicker'); - if (this.$widget) { - this.$widget.remove(); - } - delete this.$element.data().timepicker; - }, - - setDefaultTime: function(defaultTime) { - if (!this.$element.val()) { - if (defaultTime === 'current') { - var dTime = new Date(), - hours = dTime.getHours(), - minutes = dTime.getMinutes(), - seconds = dTime.getSeconds(), - meridian = 'AM'; - - if (seconds !== 0) { - seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep; - if (seconds === 60) { - minutes += 1; - seconds = 0; - } - } - - if (minutes !== 0) { - minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep; - if (minutes === 60) { - hours += 1; - minutes = 0; - } - } - - if (this.showMeridian) { - if (hours === 0) { - hours = 12; - } else if (hours >= 12) { - if (hours > 12) { - hours = hours - 12; - } - meridian = 'PM'; - } else { - meridian = 'AM'; - } - } - - this.hour = hours; - this.minute = minutes; - this.second = seconds; - this.meridian = meridian; - - this.update(); - - } else if (defaultTime === false) { - this.hour = 0; - this.minute = 0; - this.second = 0; - this.meridian = 'AM'; - } else { - this.setTime(defaultTime); - } - } else { - this.updateFromElementVal(); - } - }, - - setTime: function(time, ignoreWidget) { - if (!time) { - this.clear(); - return; - } - - var timeMode, - timeArray, - hour, - minute, - second, - meridian; - - if (typeof time === 'object' && time.getMonth){ - // this is a date object - hour = time.getHours(); - minute = time.getMinutes(); - second = time.getSeconds(); - - if (this.showMeridian){ - meridian = 'AM'; - if (hour > 12){ - meridian = 'PM'; - hour = hour % 12; - } - - if (hour === 12){ - meridian = 'PM'; - } - } - } else { - timeMode = ((/a/i).test(time) ? 1 : 0) + ((/p/i).test(time) ? 2 : 0); // 0 = none, 1 = AM, 2 = PM, 3 = BOTH. - if (timeMode > 2) { // If both are present, fail. - this.clear(); - return; - } - - timeArray = time.replace(/[^0-9\:]/g, '').split(':'); - - hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString(); - - if(this.explicitMode && hour.length > 2 && (hour.length % 2) !== 0 ) { - this.clear(); - return; - } - - minute = timeArray[1] ? timeArray[1].toString() : ''; - second = timeArray[2] ? timeArray[2].toString() : ''; - - // adaptive time parsing - if (hour.length > 4) { - second = hour.slice(-2); - hour = hour.slice(0, -2); - } - - if (hour.length > 2) { - minute = hour.slice(-2); - hour = hour.slice(0, -2); - } - - if (minute.length > 2) { - second = minute.slice(-2); - minute = minute.slice(0, -2); - } - - hour = parseInt(hour, 10); - minute = parseInt(minute, 10); - second = parseInt(second, 10); - - if (isNaN(hour)) { - hour = 0; - } - if (isNaN(minute)) { - minute = 0; - } - if (isNaN(second)) { - second = 0; - } - - // Adjust the time based upon unit boundary. - // NOTE: Negatives will never occur due to time.replace() above. - if (second > 59) { - second = 59; - } - - if (minute > 59) { - minute = 59; - } - - if (hour >= this.maxHours) { - // No day/date handling. - hour = this.maxHours - 1; - } - - if (this.showMeridian) { - if (hour > 12) { - // Force PM. - timeMode = 2; - hour -= 12; - } - if (!timeMode) { - timeMode = 1; - } - if (hour === 0) { - hour = 12; // AM or PM, reset to 12. 0 AM = 12 AM. 0 PM = 12 PM, etc. - } - meridian = timeMode === 1 ? 'AM' : 'PM'; - } else if (hour < 12 && timeMode === 2) { - hour += 12; - } else { - if (hour >= this.maxHours) { - hour = this.maxHours - 1; - } else if ((hour < 0) || (hour === 12 && timeMode === 1)){ - hour = 0; - } - } - } - - this.hour = hour; - if (this.snapToStep) { - this.minute = this.changeToNearestStep(minute, this.minuteStep); - this.second = this.changeToNearestStep(second, this.secondStep); - } else { - this.minute = minute; - this.second = second; - } - this.meridian = meridian; - - this.update(ignoreWidget); - }, - - showWidget: function() { - if (this.isOpen) { - return; - } - - if (this.$element.is(':disabled')) { - return; - } - - // show/hide approach taken by datepicker - this.$widget.appendTo(this.appendWidgetTo); - $(document).on('mousedown.timepicker, touchend.timepicker', {scope: this}, this.handleDocumentClick); - - this.$element.trigger({ - 'type': 'show.timepicker', - 'time': { - 'value': this.getTime(), - 'hours': this.hour, - 'minutes': this.minute, - 'seconds': this.second, - 'meridian': this.meridian - } - }); - - this.place(); - if (this.disableFocus) { - this.$element.blur(); - } - - // widget shouldn't be empty on open - if (this.hour === '') { - if (this.defaultTime) { - this.setDefaultTime(this.defaultTime); - } else { - this.setTime('0:0:0'); - } - } - - if (this.template === 'modal' && this.$widget.modal) { - this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this)); - } else { - if (this.isOpen === false) { - this.$widget.addClass('open'); - } - } - - this.isOpen = true; - }, - - toggleMeridian: function() { - this.meridian = this.meridian === 'AM' ? 'PM' : 'AM'; - }, - - update: function(ignoreWidget) { - this.updateElement(); - if (!ignoreWidget) { - this.updateWidget(); - } - - this.$element.trigger({ - 'type': 'changeTime.timepicker', - 'time': { - 'value': this.getTime(), - 'hours': this.hour, - 'minutes': this.minute, - 'seconds': this.second, - 'meridian': this.meridian - } - }); - }, - - updateElement: function() { - this.$element.val(this.getTime()).change(); - }, - - updateFromElementVal: function() { - this.setTime(this.$element.val()); - }, - - updateWidget: function() { - if (this.$widget === false) { - return; - } - - var hour = this.hour, - minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute, - second = this.second.toString().length === 1 ? '0' + this.second : this.second; - - if (this.showInputs) { - this.$widget.find('input.bootstrap-timepicker-hour').val(hour); - this.$widget.find('input.bootstrap-timepicker-minute').val(minute); - - if (this.showSeconds) { - this.$widget.find('input.bootstrap-timepicker-second').val(second); - } - if (this.showMeridian) { - this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian); - } - } else { - this.$widget.find('span.bootstrap-timepicker-hour').text(hour); - this.$widget.find('span.bootstrap-timepicker-minute').text(minute); - - if (this.showSeconds) { - this.$widget.find('span.bootstrap-timepicker-second').text(second); - } - if (this.showMeridian) { - this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian); - } - } - }, - - updateFromWidgetInputs: function() { - if (this.$widget === false) { - return; - } - - var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' + - this.$widget.find('input.bootstrap-timepicker-minute').val() + - (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') + - (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '') - ; - - this.setTime(t, true); - }, - - widgetClick: function(e) { - e.stopPropagation(); - e.preventDefault(); - - var $input = $(e.target), - action = $input.closest('a').data('action'); - - if (action) { - this[action](); - } - this.update(); - - if ($input.is('input')) { - $input.get(0).setSelectionRange(0,2); - } - }, - - widgetKeydown: function(e) { - var $input = $(e.target), - name = $input.attr('class').replace('bootstrap-timepicker-', ''); - - switch (e.which) { - case 9: //tab - if (e.shiftKey) { - if (name === 'hour') { - return this.hideWidget(); - } - } else if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) { - return this.hideWidget(); - } - break; - case 27: // escape - this.hideWidget(); - break; - case 38: // up arrow - e.preventDefault(); - switch (name) { - case 'hour': - this.incrementHour(); - break; - case 'minute': - this.incrementMinute(); - break; - case 'second': - this.incrementSecond(); - break; - case 'meridian': - this.toggleMeridian(); - break; - } - this.setTime(this.getTime()); - $input.get(0).setSelectionRange(0,2); - break; - case 40: // down arrow - e.preventDefault(); - switch (name) { - case 'hour': - this.decrementHour(); - break; - case 'minute': - this.decrementMinute(); - break; - case 'second': - this.decrementSecond(); - break; - case 'meridian': - this.toggleMeridian(); - break; - } - this.setTime(this.getTime()); - $input.get(0).setSelectionRange(0,2); - break; - } - }, - - widgetKeyup: function(e) { - if ((e.which === 65) || (e.which === 77) || (e.which === 80) || (e.which === 46) || (e.which === 8) || (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105)) { - this.updateFromWidgetInputs(); - } - } - }; - - //TIMEPICKER PLUGIN DEFINITION - $.fn.timepicker = function(option) { - var args = Array.apply(null, arguments); - args.shift(); - return this.each(function() { - var $this = $(this), - data = $this.data('timepicker'), - options = typeof option === 'object' && option; - - if (!data) { - $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data())))); - } - - if (typeof option === 'string') { - data[option].apply(data, args); - } - }); - }; - - $.fn.timepicker.defaults = { - defaultTime: 'current', - disableFocus: false, - disableMousewheel: false, - isOpen: false, - minuteStep: 15, - modalBackdrop: false, - orientation: { x: 'auto', y: 'auto'}, - secondStep: 15, - snapToStep: false, - showSeconds: false, - showInputs: true, - showMeridian: true, - template: 'dropdown', - appendWidgetTo: 'body', - showWidgetOnAddonClick: true, - icons: { - up: 'glyphicon glyphicon-chevron-up', - down: 'glyphicon glyphicon-chevron-down' - }, - maxHours: 24, - explicitMode: false - }; - - $.fn.timepicker.Constructor = Timepicker; - - $(document).on( - 'focus.timepicker.data-api click.timepicker.data-api', - '[data-provide="timepicker"]', - function(e){ - var $this = $(this); - if ($this.data('timepicker')) { - return; - } - e.preventDefault(); - // component click requires us to explicitly show it - $this.timepicker(); - } - ); - -})(jQuery, window, document); diff --git a/web/backend/resources/bower_components/chart.js/.bower.json b/web/backend/resources/bower_components/chart.js/.bower.json deleted file mode 100644 index 8859ddef5..000000000 --- a/web/backend/resources/bower_components/chart.js/.bower.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "Chart.js", - "version": "1.0.2", - "description": "Simple HTML5 Charts using the canvas element", - "homepage": "https://github.com/nnnick/Chart.js", - "author": "nnnick", - "main": [ - "Chart.js" - ], - "dependencies": {}, - "_release": "1.0.2", - "_resolution": { - "type": "version", - "tag": "v1.0.2", - "commit": "930b16a0af59201dcfcd1594b0e7540db4d04c9f" - }, - "_source": "https://github.com/chartjs/Chart.js.git", - "_target": "1.0.*", - "_originalSource": "chart.js" -} \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/.travis.yml b/web/backend/resources/bower_components/chart.js/.travis.yml deleted file mode 100644 index c66ea5fb7..000000000 --- a/web/backend/resources/bower_components/chart.js/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: node_js -node_js: - - "0.11" - - "0.10" - -before_script: - - npm install - -script: - - gulp test - -notifications: - slack: chartjs:pcfCZR6ugg5TEcaLtmIfQYuA diff --git a/web/backend/resources/bower_components/chart.js/CONTRIBUTING.md b/web/backend/resources/bower_components/chart.js/CONTRIBUTING.md deleted file mode 100644 index 714bdd2a7..000000000 --- a/web/backend/resources/bower_components/chart.js/CONTRIBUTING.md +++ /dev/null @@ -1,55 +0,0 @@ -Contributing to Chart.js -======================== - -Contributions to Chart.js are welcome and encouraged, but please have a look through the guidelines in this document before raising an issue, or writing code for the project. - - -Using issues ------------- - -The [issue tracker](https://github.com/nnnick/Chart.js/issues) is the preferred channel for reporting bugs, requesting new features and submitting pull requests. - -If you're suggesting a new chart type, please take a look at [writing new chart types](https://github.com/nnnick/Chart.js/blob/master/docs/06-Advanced.md#writing-new-chart-types) in the documentation, and some of the [community extensions](https://github.com/nnnick/Chart.js/blob/master/docs/06-Advanced.md#community-extensions) that have been created already. - -To keep the library lightweight for everyone, it's unlikely we'll add many more chart types to the core of Chart.js, but issues are a good medium to design and spec out how new chart types could work and look. - -Please do not use issues for support requests. For help using Chart.js, please take a look at the [`chartjs`](http://stackoverflow.com/questions/tagged/chartjs) tag on Stack Overflow. - - -Reporting bugs --------------- - -Well structured, detailed bug reports are hugely valuable for the project. - -Guidlines for reporting bugs: - - - Check the issue search to see if it has already been reported - - Isolate the problem to a simple test case - - Provide a demonstration of the problem on [JS Bin](http://jsbin.com) or similar - -Please provide any additional details associated with the bug, if it's browser or screen density specific, or only happens with a certain configuration or data. - - -Pull requests -------------- - -Clear, concise pull requests are excellent at continuing the project's community driven growth. But please review [these guidelines](https://github.com/blog/1943-how-to-write-the-perfect-pull-request) and the guidelines below before starting work on the project. - -Guidlines: - - - Please create an issue first: - - For bugs, we can discuss the fixing approach - - For enhancements, we can discuss if it is within the project scope and avoid duplicate effort - - Please make changes to the files in [`/src`](https://github.com/nnnick/Chart.js/tree/master/src), not `Chart.js` or `Chart.min.js` in the repo root directory, this avoids merge conflicts - - Tabs for indentation, not spaces please - - If adding new functionality, please also update the relevant `.md` file in [`/docs`](https://github.com/nnnick/Chart.js/tree/master/docs) - - Please make your commits in logical sections with clear commit messages - -Joining the Project -------------- - - Active committers and contributors are invited to introduce yourself and request commit access to this project. Please send an email to hello@nickdownie.com or file an issue. - -License -------- - -By contributing your code, you agree to license your contribution under the [MIT license](https://github.com/nnnick/Chart.js/blob/master/LICENSE.md). diff --git a/web/backend/resources/bower_components/chart.js/Chart.js b/web/backend/resources/bower_components/chart.js/Chart.js deleted file mode 100644 index c264262ba..000000000 --- a/web/backend/resources/bower_components/chart.js/Chart.js +++ /dev/null @@ -1,3477 +0,0 @@ -/*! - * Chart.js - * http://chartjs.org/ - * Version: 1.0.2 - * - * Copyright 2015 Nick Downie - * Released under the MIT license - * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md - */ - - -(function(){ - - "use strict"; - - //Declare root variable - window in the browser, global on the server - var root = this, - previous = root.Chart; - - //Occupy the global variable of Chart, and create a simple base class - var Chart = function(context){ - var chart = this; - this.canvas = context.canvas; - - this.ctx = context; - - //Variables global to the chart - var computeDimension = function(element,dimension) - { - if (element['offset'+dimension]) - { - return element['offset'+dimension]; - } - else - { - return document.defaultView.getComputedStyle(element).getPropertyValue(dimension); - } - } - - var width = this.width = computeDimension(context.canvas,'Width'); - var height = this.height = computeDimension(context.canvas,'Height'); - - // Firefox requires this to work correctly - context.canvas.width = width; - context.canvas.height = height; - - var width = this.width = context.canvas.width; - var height = this.height = context.canvas.height; - this.aspectRatio = this.width / this.height; - //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. - helpers.retinaScale(this); - - return this; - }; - //Globally expose the defaults to allow for user updating/changing - Chart.defaults = { - global: { - // Boolean - Whether to animate the chart - animation: true, - - // Number - Number of animation steps - animationSteps: 60, - - // String - Animation easing effect - animationEasing: "easeOutQuart", - - // Boolean - If we should show the scale at all - showScale: true, - - // Boolean - If we want to override with a hard coded scale - scaleOverride: false, - - // ** Required if scaleOverride is true ** - // Number - The number of steps in a hard coded scale - scaleSteps: null, - // Number - The value jump in the hard coded scale - scaleStepWidth: null, - // Number - The scale starting value - scaleStartValue: null, - - // String - Colour of the scale line - scaleLineColor: "rgba(0,0,0,.1)", - - // Number - Pixel width of the scale line - scaleLineWidth: 1, - - // Boolean - Whether to show labels on the scale - scaleShowLabels: true, - - // Interpolated JS string - can access value - scaleLabel: "<%=value%>", - - // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there - scaleIntegersOnly: true, - - // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero: false, - - // String - Scale label font declaration for the scale label - scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Scale label font size in pixels - scaleFontSize: 12, - - // String - Scale label font weight style - scaleFontStyle: "normal", - - // String - Scale label font colour - scaleFontColor: "#666", - - // Boolean - whether or not the chart should be responsive and resize when the browser does. - responsive: false, - - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: true, - - // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove - showTooltips: true, - - // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function - customTooltips: false, - - // Array - Array of string names to attach tooltip events - tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], - - // String - Tooltip background colour - tooltipFillColor: "rgba(0,0,0,0.8)", - - // String - Tooltip label font declaration for the scale label - tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip label font size in pixels - tooltipFontSize: 14, - - // String - Tooltip font weight style - tooltipFontStyle: "normal", - - // String - Tooltip label font colour - tooltipFontColor: "#fff", - - // String - Tooltip title font declaration for the scale label - tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip title font size in pixels - tooltipTitleFontSize: 14, - - // String - Tooltip title font weight style - tooltipTitleFontStyle: "bold", - - // String - Tooltip title font colour - tooltipTitleFontColor: "#fff", - - // Number - pixel width of padding around tooltip text - tooltipYPadding: 6, - - // Number - pixel width of padding around tooltip text - tooltipXPadding: 6, - - // Number - Size of the caret on the tooltip - tooltipCaretSize: 8, - - // Number - Pixel radius of the tooltip border - tooltipCornerRadius: 6, - - // Number - Pixel offset from point x to tooltip edge - tooltipXOffset: 10, - - // String - Template string for single tooltips - tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>", - - // String - Template string for single tooltips - multiTooltipTemplate: "<%= value %>", - - // String - Colour behind the legend colour block - multiTooltipKeyBackground: '#fff', - - // Function - Will fire on animation progression. - onAnimationProgress: function(){}, - - // Function - Will fire on animation completion. - onAnimationComplete: function(){} - - } - }; - - //Create a dictionary of chart types, to allow for extension of existing types - Chart.types = {}; - - //Global Chart helpers object for utility methods and classes - var helpers = Chart.helpers = {}; - - //-- Basic js utility methods - var each = helpers.each = function(loopable,callback,self){ - var additionalArgs = Array.prototype.slice.call(arguments, 3); - // Check to see if null or undefined firstly. - if (loopable){ - if (loopable.length === +loopable.length){ - var i; - for (i=0; i= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)){ - return currentItem; - } - } - }, - inherits = helpers.inherits = function(extensions){ - //Basic javascript inheritance based on the model created in Backbone.js - var parent = this; - var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); }; - - var Surrogate = function(){ this.constructor = ChartElement;}; - Surrogate.prototype = parent.prototype; - ChartElement.prototype = new Surrogate(); - - ChartElement.extend = inherits; - - if (extensions) extend(ChartElement.prototype, extensions); - - ChartElement.__super__ = parent.prototype; - - return ChartElement; - }, - noop = helpers.noop = function(){}, - uid = helpers.uid = (function(){ - var id=0; - return function(){ - return "chart-" + id++; - }; - })(), - warn = helpers.warn = function(str){ - //Method for warning of errors - if (window.console && typeof window.console.warn == "function") console.warn(str); - }, - amd = helpers.amd = (typeof define == 'function' && define.amd), - //-- Math methods - isNumber = helpers.isNumber = function(n){ - return !isNaN(parseFloat(n)) && isFinite(n); - }, - max = helpers.max = function(array){ - return Math.max.apply( Math, array ); - }, - min = helpers.min = function(array){ - return Math.min.apply( Math, array ); - }, - cap = helpers.cap = function(valueToCap,maxValue,minValue){ - if(isNumber(maxValue)) { - if( valueToCap > maxValue ) { - return maxValue; - } - } - else if(isNumber(minValue)){ - if ( valueToCap < minValue ){ - return minValue; - } - } - return valueToCap; - }, - getDecimalPlaces = helpers.getDecimalPlaces = function(num){ - if (num%1!==0 && isNumber(num)){ - return num.toString().split(".")[1].length; - } - else { - return 0; - } - }, - toRadians = helpers.radians = function(degrees){ - return degrees * (Math.PI/180); - }, - // Gets the angle from vertical upright to the point about a centre. - getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){ - var distanceFromXCenter = anglePoint.x - centrePoint.x, - distanceFromYCenter = anglePoint.y - centrePoint.y, - radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - - var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter); - - //If the segment is in the top left quadrant, we need to add another rotation to the angle - if (distanceFromXCenter < 0 && distanceFromYCenter < 0){ - angle += Math.PI*2; - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }, - aliasPixel = helpers.aliasPixel = function(pixelWidth){ - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }, - splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){ - //Props to Rob Spencer at scaled innovation for his post on splining between points - //http://scaledinnovation.com/analytics/splines/aboutSplines.html - var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)), - d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)), - fa=t*d01/(d01+d12),// scaling factor for triangle Ta - fb=t*d12/(d01+d12); - return { - inner : { - x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y) - }, - outer : { - x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y) - } - }; - }, - calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){ - return Math.floor(Math.log(val) / Math.LN10); - }, - calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){ - - //Set a minimum step of two - a point at the top of the graph, and a point at the base - var minSteps = 2, - maxSteps = Math.floor(drawingSize/(textSize * 1.5)), - skipFitting = (minSteps >= maxSteps); - - var maxValue = max(valuesArray), - minValue = min(valuesArray); - - // We need some degree of seperation here to calculate the scales if all the values are the same - // Adding/minusing 0.5 will give us a range of 1. - if (maxValue === minValue){ - maxValue += 0.5; - // So we don't end up with a graph with a negative start value if we've said always start from zero - if (minValue >= 0.5 && !startFromZero){ - minValue -= 0.5; - } - else{ - // Make up a whole number above the values - maxValue += 0.5; - } - } - - var valueRange = Math.abs(maxValue - minValue), - rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange), - graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphRange = graphMax - graphMin, - stepValue = Math.pow(10, rangeOrderOfMagnitude), - numberOfSteps = Math.round(graphRange / stepValue); - - //If we have more space on the graph we'll use it to give more definition to the data - while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) { - if(numberOfSteps > maxSteps){ - stepValue *=2; - numberOfSteps = Math.round(graphRange/stepValue); - // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps. - if (numberOfSteps % 1 !== 0){ - skipFitting = true; - } - } - //We can fit in double the amount of scale points on the scale - else{ - //If user has declared ints only, and the step value isn't a decimal - if (integersOnly && rangeOrderOfMagnitude >= 0){ - //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float - if(stepValue/2 % 1 === 0){ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - //If it would make it a float break out of the loop - else{ - break; - } - } - //If the scale doesn't have to be an int, make the scale more granular anyway. - else{ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - - } - } - - if (skipFitting){ - numberOfSteps = minSteps; - stepValue = graphRange / numberOfSteps; - } - - return { - steps : numberOfSteps, - stepValue : stepValue, - min : graphMin, - max : graphMin + (numberOfSteps * stepValue) - }; - - }, - /* jshint ignore:start */ - // Blows up jshint errors based on the new Function constructor - //Templating methods - //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ - template = helpers.template = function(templateString, valuesObject){ - - // If templateString is function rather than string-template - call the function for valuesObject - - if(templateString instanceof Function){ - return templateString(valuesObject); - } - - var cache = {}; - function tmpl(str, data){ - // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. - var fn = !/\W/.test(str) ? - cache[str] = cache[str] : - - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - new Function("obj", - "var p=[],print=function(){p.push.apply(p,arguments);};" + - - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + - - // Convert the template into pure JavaScript - str - .replace(/[\r\t\n]/g, " ") - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)'/g, "$1\r") - .replace(/\t=(.*?)%>/g, "',$1,'") - .split("\t").join("');") - .split("%>").join("p.push('") - .split("\r").join("\\'") + - "');}return p.join('');" - ); - - // Provide some basic currying to the user - return data ? fn( data ) : fn; - } - return tmpl(templateString,valuesObject); - }, - /* jshint ignore:end */ - generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){ - var labelsArray = new Array(numberOfSteps); - if (labelTemplateString){ - each(labelsArray,function(val,index){ - labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))}); - }); - } - return labelsArray; - }, - //--Animation methods - //Easing functions adapted from Robert Penner's easing equations - //http://www.robertpenner.com/easing/ - easingEffects = helpers.easingEffects = { - linear: function (t) { - return t; - }, - easeInQuad: function (t) { - return t * t; - }, - easeOutQuad: function (t) { - return -1 * t * (t - 2); - }, - easeInOutQuad: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t; - return -1 / 2 * ((--t) * (t - 2) - 1); - }, - easeInCubic: function (t) { - return t * t * t; - }, - easeOutCubic: function (t) { - return 1 * ((t = t / 1 - 1) * t * t + 1); - }, - easeInOutCubic: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t; - return 1 / 2 * ((t -= 2) * t * t + 2); - }, - easeInQuart: function (t) { - return t * t * t * t; - }, - easeOutQuart: function (t) { - return -1 * ((t = t / 1 - 1) * t * t * t - 1); - }, - easeInOutQuart: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t; - return -1 / 2 * ((t -= 2) * t * t * t - 2); - }, - easeInQuint: function (t) { - return 1 * (t /= 1) * t * t * t * t; - }, - easeOutQuint: function (t) { - return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); - }, - easeInOutQuint: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t; - return 1 / 2 * ((t -= 2) * t * t * t * t + 2); - }, - easeInSine: function (t) { - return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; - }, - easeOutSine: function (t) { - return 1 * Math.sin(t / 1 * (Math.PI / 2)); - }, - easeInOutSine: function (t) { - return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); - }, - easeInExpo: function (t) { - return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); - }, - easeOutExpo: function (t) { - return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); - }, - easeInOutExpo: function (t) { - if (t === 0) return 0; - if (t === 1) return 1; - if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1)); - return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); - }, - easeInCirc: function (t) { - if (t >= 1) return t; - return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); - }, - easeOutCirc: function (t) { - return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); - }, - easeInOutCirc: function (t) { - if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1); - return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - easeInElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - }, - easeOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; - }, - easeInOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1 / 2) == 2) return 1; - if (!p) p = 1 * (0.3 * 1.5); - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function (t) { - var s = 1.70158; - return 1 * (t /= 1) * t * ((s + 1) * t - s); - }, - easeOutBack: function (t) { - var s = 1.70158; - return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); - }, - easeInOutBack: function (t) { - var s = 1.70158; - if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); - return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - easeInBounce: function (t) { - return 1 - easingEffects.easeOutBounce(1 - t); - }, - easeOutBounce: function (t) { - if ((t /= 1) < (1 / 2.75)) { - return 1 * (7.5625 * t * t); - } else if (t < (2 / 2.75)) { - return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); - } else if (t < (2.5 / 2.75)) { - return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); - } else { - return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); - } - }, - easeInOutBounce: function (t) { - if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5; - return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; - } - }, - //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - requestAnimFrame = helpers.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - })(), - cancelAnimFrame = helpers.cancelAnimFrame = (function(){ - return window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(callback) { - return window.clearTimeout(callback, 1000 / 60); - }; - })(), - animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){ - - var currentStep = 0, - easingFunction = easingEffects[easingString] || easingEffects.linear; - - var animationFrame = function(){ - currentStep++; - var stepDecimal = currentStep/totalSteps; - var easeDecimal = easingFunction(stepDecimal); - - callback.call(chartInstance,easeDecimal,stepDecimal, currentStep); - onProgress.call(chartInstance,easeDecimal,stepDecimal); - if (currentStep < totalSteps){ - chartInstance.animationFrame = requestAnimFrame(animationFrame); - } else{ - onComplete.apply(chartInstance); - } - }; - requestAnimFrame(animationFrame); - }, - //-- DOM methods - getRelativePosition = helpers.getRelativePosition = function(evt){ - var mouseX, mouseY; - var e = evt.originalEvent || evt, - canvas = evt.currentTarget || evt.srcElement, - boundingRect = canvas.getBoundingClientRect(); - - if (e.touches){ - mouseX = e.touches[0].clientX - boundingRect.left; - mouseY = e.touches[0].clientY - boundingRect.top; - - } - else{ - mouseX = e.clientX - boundingRect.left; - mouseY = e.clientY - boundingRect.top; - } - - return { - x : mouseX, - y : mouseY - }; - - }, - addEvent = helpers.addEvent = function(node,eventType,method){ - if (node.addEventListener){ - node.addEventListener(eventType,method); - } else if (node.attachEvent){ - node.attachEvent("on"+eventType, method); - } else { - node["on"+eventType] = method; - } - }, - removeEvent = helpers.removeEvent = function(node, eventType, handler){ - if (node.removeEventListener){ - node.removeEventListener(eventType, handler, false); - } else if (node.detachEvent){ - node.detachEvent("on"+eventType,handler); - } else{ - node["on" + eventType] = noop; - } - }, - bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){ - // Create the events object if it's not already present - if (!chartInstance.events) chartInstance.events = {}; - - each(arrayOfEvents,function(eventName){ - chartInstance.events[eventName] = function(){ - handler.apply(chartInstance, arguments); - }; - addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]); - }); - }, - unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) { - each(arrayOfEvents, function(handler,eventName){ - removeEvent(chartInstance.chart.canvas, eventName, handler); - }); - }, - getMaximumWidth = helpers.getMaximumWidth = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientWidth; - }, - getMaximumHeight = helpers.getMaximumHeight = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientHeight; - }, - getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support - retinaScale = helpers.retinaScale = function(chart){ - var ctx = chart.ctx, - width = chart.canvas.width, - height = chart.canvas.height; - - if (window.devicePixelRatio) { - ctx.canvas.style.width = width + "px"; - ctx.canvas.style.height = height + "px"; - ctx.canvas.height = height * window.devicePixelRatio; - ctx.canvas.width = width * window.devicePixelRatio; - ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - } - }, - //-- Canvas methods - clear = helpers.clear = function(chart){ - chart.ctx.clearRect(0,0,chart.width,chart.height); - }, - fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){ - return fontStyle + " " + pixelSize+"px " + fontFamily; - }, - longestText = helpers.longestText = function(ctx,font,arrayOfStrings){ - ctx.font = font; - var longest = 0; - each(arrayOfStrings,function(string){ - var textWidth = ctx.measureText(string).width; - longest = (textWidth > longest) ? textWidth : longest; - }); - return longest; - }, - drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){ - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - }; - - - //Store a reference to each instance - allowing us to globally resize chart instances on window resize. - //Destroy method on the chart will remove the instance of the chart from this reference. - Chart.instances = {}; - - Chart.Type = function(data,options,chart){ - this.options = options; - this.chart = chart; - this.id = uid(); - //Add the chart instance to the global namespace - Chart.instances[this.id] = this; - - // Initialize is always called when a chart type is created - // By default it is a no op, but it should be extended - if (options.responsive){ - this.resize(); - } - this.initialize.call(this,data); - }; - - //Core methods that'll be a part of every chart type - extend(Chart.Type.prototype,{ - initialize : function(){return this;}, - clear : function(){ - clear(this.chart); - return this; - }, - stop : function(){ - // Stops any current animation loop occuring - cancelAnimFrame(this.animationFrame); - return this; - }, - resize : function(callback){ - this.stop(); - var canvas = this.chart.canvas, - newWidth = getMaximumWidth(this.chart.canvas), - newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas); - - canvas.width = this.chart.width = newWidth; - canvas.height = this.chart.height = newHeight; - - retinaScale(this.chart); - - if (typeof callback === "function"){ - callback.apply(this, Array.prototype.slice.call(arguments, 1)); - } - return this; - }, - reflow : noop, - render : function(reflow){ - if (reflow){ - this.reflow(); - } - if (this.options.animation && !reflow){ - helpers.animationLoop( - this.draw, - this.options.animationSteps, - this.options.animationEasing, - this.options.onAnimationProgress, - this.options.onAnimationComplete, - this - ); - } - else{ - this.draw(); - this.options.onAnimationComplete.call(this); - } - return this; - }, - generateLegend : function(){ - return template(this.options.legendTemplate,this); - }, - destroy : function(){ - this.clear(); - unbindEvents(this, this.events); - var canvas = this.chart.canvas; - - // Reset canvas height/width attributes starts a fresh with the canvas context - canvas.width = this.chart.width; - canvas.height = this.chart.height; - - // < IE9 doesn't support removeProperty - if (canvas.style.removeProperty) { - canvas.style.removeProperty('width'); - canvas.style.removeProperty('height'); - } else { - canvas.style.removeAttribute('width'); - canvas.style.removeAttribute('height'); - } - - delete Chart.instances[this.id]; - }, - showTooltip : function(ChartElements, forceRedraw){ - // Only redraw the chart if we've actually changed what we're hovering on. - if (typeof this.activeElements === 'undefined') this.activeElements = []; - - var isChanged = (function(Elements){ - var changed = false; - - if (Elements.length !== this.activeElements.length){ - changed = true; - return changed; - } - - each(Elements, function(element, index){ - if (element !== this.activeElements[index]){ - changed = true; - } - }, this); - return changed; - }).call(this, ChartElements); - - if (!isChanged && !forceRedraw){ - return; - } - else{ - this.activeElements = ChartElements; - } - this.draw(); - if(this.options.customTooltips){ - this.options.customTooltips(false); - } - if (ChartElements.length > 0){ - // If we have multiple datasets, show a MultiTooltip for all of the data points at that index - if (this.datasets && this.datasets.length > 1) { - var dataArray, - dataIndex; - - for (var i = this.datasets.length - 1; i >= 0; i--) { - dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments; - dataIndex = indexOf(dataArray, ChartElements[0]); - if (dataIndex !== -1){ - break; - } - } - var tooltipLabels = [], - tooltipColors = [], - medianPosition = (function(index) { - - // Get all the points at that particular index - var Elements = [], - dataCollection, - xPositions = [], - yPositions = [], - xMax, - yMax, - xMin, - yMin; - helpers.each(this.datasets, function(dataset){ - dataCollection = dataset.points || dataset.bars || dataset.segments; - if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){ - Elements.push(dataCollection[dataIndex]); - } - }); - - helpers.each(Elements, function(element) { - xPositions.push(element.x); - yPositions.push(element.y); - - - //Include any colour information about the element - tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element)); - tooltipColors.push({ - fill: element._saved.fillColor || element.fillColor, - stroke: element._saved.strokeColor || element.strokeColor - }); - - }, this); - - yMin = min(yPositions); - yMax = max(yPositions); - - xMin = min(xPositions); - xMax = max(xPositions); - - return { - x: (xMin > this.chart.width/2) ? xMin : xMax, - y: (yMin + yMax)/2 - }; - }).call(this, dataIndex); - - new Chart.MultiTooltip({ - x: medianPosition.x, - y: medianPosition.y, - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - xOffset: this.options.tooltipXOffset, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - titleTextColor: this.options.tooltipTitleFontColor, - titleFontFamily: this.options.tooltipTitleFontFamily, - titleFontStyle: this.options.tooltipTitleFontStyle, - titleFontSize: this.options.tooltipTitleFontSize, - cornerRadius: this.options.tooltipCornerRadius, - labels: tooltipLabels, - legendColors: tooltipColors, - legendColorBackground : this.options.multiTooltipKeyBackground, - title: ChartElements[0].label, - chart: this.chart, - ctx: this.chart.ctx, - custom: this.options.customTooltips - }).draw(); - - } else { - each(ChartElements, function(Element) { - var tooltipPosition = Element.tooltipPosition(); - new Chart.Tooltip({ - x: Math.round(tooltipPosition.x), - y: Math.round(tooltipPosition.y), - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - caretHeight: this.options.tooltipCaretSize, - cornerRadius: this.options.tooltipCornerRadius, - text: template(this.options.tooltipTemplate, Element), - chart: this.chart, - custom: this.options.customTooltips - }).draw(); - }, this); - } - } - return this; - }, - toBase64Image : function(){ - return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); - } - }); - - Chart.Type.extend = function(extensions){ - - var parent = this; - - var ChartType = function(){ - return parent.apply(this,arguments); - }; - - //Copy the prototype object of the this class - ChartType.prototype = clone(parent.prototype); - //Now overwrite some of the properties in the base class with the new extensions - extend(ChartType.prototype, extensions); - - ChartType.extend = Chart.Type.extend; - - if (extensions.name || parent.prototype.name){ - - var chartName = extensions.name || parent.prototype.name; - //Assign any potential default values of the new chart type - - //If none are defined, we'll use a clone of the chart type this is being extended from. - //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart - //doesn't define some defaults of their own. - - var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {}; - - Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults); - - Chart.types[chartName] = ChartType; - - //Register this new chart type in the Chart prototype - Chart.prototype[chartName] = function(data,options){ - var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {}); - return new ChartType(data,config,this); - }; - } else{ - warn("Name not provided for this chart, so it hasn't been registered"); - } - return parent; - }; - - Chart.Element = function(configuration){ - extend(this,configuration); - this.initialize.apply(this,arguments); - this.save(); - }; - extend(Chart.Element.prototype,{ - initialize : function(){}, - restore : function(props){ - if (!props){ - extend(this,this._saved); - } else { - each(props,function(key){ - this[key] = this._saved[key]; - },this); - } - return this; - }, - save : function(){ - this._saved = clone(this); - delete this._saved._saved; - return this; - }, - update : function(newProps){ - each(newProps,function(value,key){ - this._saved[key] = this[key]; - this[key] = value; - },this); - return this; - }, - transition : function(props,ease){ - each(props,function(value,key){ - this[key] = ((value - this._saved[key]) * ease) + this._saved[key]; - },this); - return this; - }, - tooltipPosition : function(){ - return { - x : this.x, - y : this.y - }; - }, - hasValue: function(){ - return isNumber(this.value); - } - }); - - Chart.Element.extend = inherits; - - - Chart.Point = Chart.Element.extend({ - display: true, - inRange: function(chartX,chartY){ - var hitDetectionRange = this.hitDetectionRadius + this.radius; - return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2)); - }, - draw : function(){ - if (this.display){ - var ctx = this.ctx; - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); - ctx.closePath(); - - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.stroke(); - } - - - //Quick debug for bezier curve splining - //Highlights control points and the line between them. - //Handy for dev - stripped in the min version. - - // ctx.save(); - // ctx.fillStyle = "black"; - // ctx.strokeStyle = "black" - // ctx.beginPath(); - // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.beginPath(); - // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y); - // ctx.lineTo(this.x, this.y); - // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y); - // ctx.stroke(); - - // ctx.restore(); - - - - } - }); - - Chart.Arc = Chart.Element.extend({ - inRange : function(chartX,chartY){ - - var pointRelativePosition = helpers.getAngleFromPoint(this, { - x: chartX, - y: chartY - }); - - //Check if within the range of the open/close angle - var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle), - withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius); - - return (betweenAngles && withinRadius); - //Ensure within the outside of the arc centre, but inside arc outer - }, - tooltipPosition : function(){ - var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2), - rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius; - return { - x : this.x + (Math.cos(centreAngle) * rangeFromCentre), - y : this.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - draw : function(animationPercent){ - - var easingDecimal = animationPercent || 1; - - var ctx = this.ctx; - - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle); - - ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true); - - ctx.closePath(); - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.lineJoin = 'bevel'; - - if (this.showStroke){ - ctx.stroke(); - } - } - }); - - Chart.Rectangle = Chart.Element.extend({ - draw : function(){ - var ctx = this.ctx, - halfWidth = this.width/2, - leftX = this.x - halfWidth, - rightX = this.x + halfWidth, - top = this.base - (this.base - this.y), - halfStroke = this.strokeWidth / 2; - - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (this.showStroke){ - leftX += halfStroke; - rightX -= halfStroke; - top += halfStroke; - } - - ctx.beginPath(); - - ctx.fillStyle = this.fillColor; - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - // It'd be nice to keep this class totally generic to any rectangle - // and simply specify which border to miss out. - ctx.moveTo(leftX, this.base); - ctx.lineTo(leftX, top); - ctx.lineTo(rightX, top); - ctx.lineTo(rightX, this.base); - ctx.fill(); - if (this.showStroke){ - ctx.stroke(); - } - }, - height : function(){ - return this.base - this.y; - }, - inRange : function(chartX,chartY){ - return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base); - } - }); - - Chart.Tooltip = Chart.Element.extend({ - draw : function(){ - - var ctx = this.chart.ctx; - - ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.xAlign = "center"; - this.yAlign = "above"; - - //Distance between the actual element.y position and the start of the tooltip caret - var caretPadding = this.caretPadding = 2; - - var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, - tooltipRectHeight = this.fontSize + 2*this.yPadding, - tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding; - - if (this.x + tooltipWidth/2 >this.chart.width){ - this.xAlign = "left"; - } else if (this.x - tooltipWidth/2 < 0){ - this.xAlign = "right"; - } - - if (this.y - tooltipHeight < 0){ - this.yAlign = "below"; - } - - - var tooltipX = this.x - tooltipWidth/2, - tooltipY = this.y - tooltipHeight; - - ctx.fillStyle = this.fillColor; - - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - switch(this.yAlign) - { - case "above": - //Draw a caret above the x/y - ctx.beginPath(); - ctx.moveTo(this.x,this.y - caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.closePath(); - ctx.fill(); - break; - case "below": - tooltipY = this.y + caretPadding + this.caretHeight; - //Draw a caret below the x/y - ctx.beginPath(); - ctx.moveTo(this.x, this.y + caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.closePath(); - ctx.fill(); - break; - } - - switch(this.xAlign) - { - case "left": - tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); - break; - case "right": - tooltipX = this.x - (this.cornerRadius + this.caretHeight); - break; - } - - drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); - - ctx.fill(); - - ctx.fillStyle = this.textColor; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); - } - } - }); - - Chart.MultiTooltip = Chart.Element.extend({ - initialize : function(){ - this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily); - - this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5; - - this.ctx.font = this.titleFont; - - var titleWidth = this.ctx.measureText(this.title).width, - //Label has a legend square as well so account for this. - labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3, - longestTextWidth = max([labelWidth,titleWidth]); - - this.width = longestTextWidth + (this.xPadding*2); - - - var halfHeight = this.height/2; - - //Check to ensure the height will fit on the canvas - if (this.y - halfHeight < 0 ){ - this.y = halfHeight; - } else if (this.y + halfHeight > this.chart.height){ - this.y = this.chart.height - halfHeight; - } - - //Decide whether to align left or right based on position on canvas - if (this.x > this.chart.width/2){ - this.x -= this.xOffset + this.width; - } else { - this.x += this.xOffset; - } - - - }, - getLineHeight : function(index){ - var baseLineHeight = this.y - (this.height/2) + this.yPadding, - afterTitleIndex = index-1; - - //If the index is zero, we're getting the title - if (index === 0){ - return baseLineHeight + this.titleFontSize/2; - } else{ - return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5; - } - - }, - draw : function(){ - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); - var ctx = this.ctx; - ctx.fillStyle = this.fillColor; - ctx.fill(); - ctx.closePath(); - - ctx.textAlign = "left"; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.titleTextColor; - ctx.font = this.titleFont; - - ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); - - ctx.font = this.font; - helpers.each(this.labels,function(label,index){ - ctx.fillStyle = this.textColor; - ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); - - //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) - //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - //Instead we'll make a white filled block to put the legendColour palette over. - - ctx.fillStyle = this.legendColorBackground; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - ctx.fillStyle = this.legendColors[index].fill; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - - },this); - } - } - }); - - Chart.Scale = Chart.Element.extend({ - initialize : function(){ - this.fit(); - }, - buildYLabels : function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0; - }, - addXLabel : function(label){ - this.xLabels.push(label); - this.valuesCount++; - this.fit(); - }, - removeXLabel : function(){ - this.xLabels.shift(); - this.valuesCount--; - this.fit(); - }, - // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use - fit: function(){ - // First we need the width of the yLabels, assuming the xLabels aren't rotated - - // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation - this.startPoint = (this.display) ? this.fontSize : 0; - this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels - - // Apply padding settings to the start and end point. - this.startPoint += this.padding; - this.endPoint -= this.padding; - - // Cache the starting height, so can determine if we need to recalculate the scale yAxis - var cachedHeight = this.endPoint - this.startPoint, - cachedYLabelWidth; - - // Build the current yLabels so we have an idea of what size they'll be to start - /* - * This sets what is returned from calculateScaleRange as static properties of this class: - * - this.steps; - this.stepValue; - this.min; - this.max; - * - */ - this.calculateYRange(cachedHeight); - - // With these properties set we can now build the array of yLabels - // and also the width of the largest yLabel - this.buildYLabels(); - - this.calculateXLabelRotation(); - - while((cachedHeight > this.endPoint - this.startPoint)){ - cachedHeight = this.endPoint - this.startPoint; - cachedYLabelWidth = this.yLabelWidth; - - this.calculateYRange(cachedHeight); - this.buildYLabels(); - - // Only go through the xLabel loop again if the yLabel width has changed - if (cachedYLabelWidth < this.yLabelWidth){ - this.calculateXLabelRotation(); - } - } - - }, - calculateXLabelRotation : function(){ - //Get the width of each grid by calculating the difference - //between x offsets between 0 and 1. - - this.ctx.font = this.font; - - var firstWidth = this.ctx.measureText(this.xLabels[0]).width, - lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width, - firstRotated, - lastRotated; - - - this.xScalePaddingRight = lastWidth/2 + 3; - this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10; - - this.xLabelRotation = 0; - if (this.display){ - var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels), - cosRotation, - firstRotatedWidth; - this.xLabelWidth = originalLabelWidth; - //Allow 3 pixels x2 padding either side for label readability - var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6; - - //Max label rotate should be 90 - also act as a loop counter - while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){ - cosRotation = Math.cos(toRadians(this.xLabelRotation)); - - firstRotated = cosRotation * firstWidth; - lastRotated = cosRotation * lastWidth; - - // We're right aligning the text now. - if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){ - this.xScalePaddingLeft = firstRotated + this.fontSize / 2; - } - this.xScalePaddingRight = this.fontSize/2; - - - this.xLabelRotation++; - this.xLabelWidth = cosRotation * originalLabelWidth; - - } - if (this.xLabelRotation > 0){ - this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3; - } - } - else{ - this.xLabelWidth = 0; - this.xScalePaddingRight = this.padding; - this.xScalePaddingLeft = this.padding; - } - - }, - // Needs to be overidden in each Chart type - // Otherwise we need to pass all the data into the scale class - calculateYRange: noop, - drawingArea: function(){ - return this.startPoint - this.endPoint; - }, - calculateY : function(value){ - var scalingFactor = this.drawingArea() / (this.min - this.max); - return this.endPoint - (scalingFactor * (value - this.min)); - }, - calculateX : function(index){ - var isRotated = (this.xLabelRotation > 0), - // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding, - innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight), - valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1), - valueOffset = (valueWidth * index) + this.xScalePaddingLeft; - - if (this.offsetGridLines){ - valueOffset += (valueWidth/2); - } - - return Math.round(valueOffset); - }, - update : function(newProps){ - helpers.extend(this, newProps); - this.fit(); - }, - draw : function(){ - var ctx = this.ctx, - yLabelGap = (this.endPoint - this.startPoint) / this.steps, - xStart = Math.round(this.xScalePaddingLeft); - if (this.display){ - ctx.fillStyle = this.textColor; - ctx.font = this.font; - each(this.yLabels,function(labelString,index){ - var yLabelCenter = this.endPoint - (yLabelGap * index), - linePositionY = Math.round(yLabelCenter), - drawHorizontalLine = this.showHorizontalLines; - - ctx.textAlign = "right"; - ctx.textBaseline = "middle"; - if (this.showLabels){ - ctx.fillText(labelString,xStart - 10,yLabelCenter); - } - - // This is X axis, so draw it - if (index === 0 && !drawHorizontalLine){ - drawHorizontalLine = true; - } - - if (drawHorizontalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - linePositionY += helpers.aliasPixel(ctx.lineWidth); - - if(drawHorizontalLine){ - ctx.moveTo(xStart, linePositionY); - ctx.lineTo(this.width, linePositionY); - ctx.stroke(); - ctx.closePath(); - } - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - ctx.beginPath(); - ctx.moveTo(xStart - 5, linePositionY); - ctx.lineTo(xStart, linePositionY); - ctx.stroke(); - ctx.closePath(); - - },this); - - each(this.xLabels,function(label,index){ - var xPos = this.calculateX(index) + aliasPixel(this.lineWidth), - // Check to see if line/bar here and decide where to place the line - linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth), - isRotated = (this.xLabelRotation > 0), - drawVerticalLine = this.showVerticalLines; - - // This is Y axis, so draw it - if (index === 0 && !drawVerticalLine){ - drawVerticalLine = true; - } - - if (drawVerticalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - if (drawVerticalLine){ - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.startPoint - 3); - ctx.stroke(); - ctx.closePath(); - } - - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - - - // Small lines at the bottom of the base grid line - ctx.beginPath(); - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.endPoint + 5); - ctx.stroke(); - ctx.closePath(); - - ctx.save(); - ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8); - ctx.rotate(toRadians(this.xLabelRotation)*-1); - ctx.font = this.font; - ctx.textAlign = (isRotated) ? "right" : "center"; - ctx.textBaseline = (isRotated) ? "middle" : "top"; - ctx.fillText(label, 0, 0); - ctx.restore(); - },this); - - } - } - - }); - - Chart.RadialScale = Chart.Element.extend({ - initialize: function(){ - this.size = min([this.height, this.width]); - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - }, - calculateCenterOffset: function(value){ - // Take into account half font size + the yPadding of the top value - var scalingFactor = this.drawingArea / (this.max - this.min); - - return (value - this.min) * scalingFactor; - }, - update : function(){ - if (!this.lineArc){ - this.setScaleSize(); - } else { - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - } - this.buildYLabels(); - }, - buildYLabels: function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - }, - getCircumference : function(){ - return ((Math.PI*2) / this.valuesCount); - }, - setScaleSize: function(){ - /* - * Right, this is really confusing and there is a lot of maths going on here - * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - * - * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - * - * Solution: - * - * We assume the radius of the polygon is half the size of the canvas at first - * at each index we check if the text overlaps. - * - * Where it does, we store that angle and that index. - * - * After finding the largest index and angle we calculate how much we need to remove - * from the shape radius to move the point inwards by that x. - * - * We average the left and right distances to get the maximum shape radius that can fit in the box - * along with labels. - * - * Once we have that, we can find the centre point for the chart, by taking the x text protrusion - * on each side, removing that from the size, halving it and adding the left x protrusion width. - * - * This will mean we have a shape fitted to the canvas, as large as it can be with the labels - * and position it in the most space efficient manner - * - * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - */ - - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]), - pointPosition, - i, - textWidth, - halfTextWidth, - furthestRight = this.width, - furthestRightIndex, - furthestRightAngle, - furthestLeft = 0, - furthestLeftIndex, - furthestLeftAngle, - xProtrusionLeft, - xProtrusionRight, - radiusReductionRight, - radiusReductionLeft, - maxWidthRadius; - this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - for (i=0;i furthestRight) { - furthestRight = pointPosition.x + halfTextWidth; - furthestRightIndex = i; - } - if (pointPosition.x - halfTextWidth < furthestLeft) { - furthestLeft = pointPosition.x - halfTextWidth; - furthestLeftIndex = i; - } - } - else if (i < this.valuesCount/2) { - // Less than half the values means we'll left align the text - if (pointPosition.x + textWidth > furthestRight) { - furthestRight = pointPosition.x + textWidth; - furthestRightIndex = i; - } - } - else if (i > this.valuesCount/2){ - // More than half the values means we'll right align the text - if (pointPosition.x - textWidth < furthestLeft) { - furthestLeft = pointPosition.x - textWidth; - furthestLeftIndex = i; - } - } - } - - xProtrusionLeft = furthestLeft; - - xProtrusionRight = Math.ceil(furthestRight - this.width); - - furthestRightAngle = this.getIndexAngle(furthestRightIndex); - - furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); - - radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2); - - radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2); - - // Ensure we actually need to reduce the size of the chart - radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0; - radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; - - this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2; - - //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2]) - this.setCenterPoint(radiusReductionLeft, radiusReductionRight); - - }, - setCenterPoint: function(leftMovement, rightMovement){ - - var maxRight = this.width - rightMovement - this.drawingArea, - maxLeft = leftMovement + this.drawingArea; - - this.xCenter = (maxLeft + maxRight)/2; - // Always vertically in the centre as the text height doesn't change - this.yCenter = (this.height/2); - }, - - getIndexAngle : function(index){ - var angleMultiplier = (Math.PI * 2) / this.valuesCount; - // Start from the top instead of right, so remove a quarter of the circle - - return index * angleMultiplier - (Math.PI/2); - }, - getPointPosition : function(index, distanceFromCenter){ - var thisAngle = this.getIndexAngle(index); - return { - x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter, - y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter - }; - }, - draw: function(){ - if (this.display){ - var ctx = this.ctx; - each(this.yLabels, function(label, index){ - // Don't draw a centre value - if (index > 0){ - var yCenterOffset = index * (this.drawingArea/this.steps), - yHeight = this.yCenter - yCenterOffset, - pointPosition; - - // Draw circular lines around the scale - if (this.lineWidth > 0){ - ctx.strokeStyle = this.lineColor; - ctx.lineWidth = this.lineWidth; - - if(this.lineArc){ - ctx.beginPath(); - ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2); - ctx.closePath(); - ctx.stroke(); - } else{ - ctx.beginPath(); - for (var i=0;i= 0; i--) { - if (this.angleLineWidth > 0){ - var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max)); - ctx.beginPath(); - ctx.moveTo(this.xCenter, this.yCenter); - ctx.lineTo(outerPosition.x, outerPosition.y); - ctx.stroke(); - ctx.closePath(); - } - // Extra 3px out for some label spacing - var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5); - ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - ctx.fillStyle = this.pointLabelFontColor; - - var labelsCount = this.labels.length, - halfLabelsCount = this.labels.length/2, - quarterLabelsCount = halfLabelsCount/2, - upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), - exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); - if (i === 0){ - ctx.textAlign = 'center'; - } else if(i === halfLabelsCount){ - ctx.textAlign = 'center'; - } else if (i < halfLabelsCount){ - ctx.textAlign = 'left'; - } else { - ctx.textAlign = 'right'; - } - - // Set the correct text baseline based on outer positioning - if (exactQuarter){ - ctx.textBaseline = 'middle'; - } else if (upperHalf){ - ctx.textBaseline = 'bottom'; - } else { - ctx.textBaseline = 'top'; - } - - ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y); - } - } - } - } - }); - - // Attach global event to resize each chart instance when the browser resizes - helpers.addEvent(window, "resize", (function(){ - // Basic debounce of resize function so it doesn't hurt performance when resizing browser. - var timeout; - return function(){ - clearTimeout(timeout); - timeout = setTimeout(function(){ - each(Chart.instances,function(instance){ - // If the responsive flag is set in the chart instance config - // Cascade the resize event down to the chart. - if (instance.options.responsive){ - instance.resize(instance.render, true); - } - }); - }, 50); - }; - })()); - - - if (amd) { - define(function(){ - return Chart; - }); - } else if (typeof module === 'object' && module.exports) { - module.exports = Chart; - } - - root.Chart = Chart; - - Chart.noConflict = function(){ - root.Chart = previous; - return Chart; - }; - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - var defaultConfig = { - //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero : true, - - //Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - If there is a stroke on each bar - barShowStroke : true, - - //Number - Pixel width of the bar stroke - barStrokeWidth : 2, - - //Number - Spacing between each of the X value sets - barValueSpacing : 5, - - //Number - Spacing between data sets within X values - barDatasetSpacing : 1, - - //String - A legend template - legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" - - }; - - - Chart.Type.extend({ - name: "Bar", - defaults : defaultConfig, - initialize: function(data){ - - //Expose options as a scope variable here so we can access it in the ScaleClass - var options = this.options; - - this.ScaleClass = Chart.Scale.extend({ - offsetGridLines : true, - calculateBarX : function(datasetCount, datasetIndex, barIndex){ - //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar - var xWidth = this.calculateBaseWidth(), - xAbsolute = this.calculateX(barIndex) - (xWidth/2), - barWidth = this.calculateBarWidth(datasetCount); - - return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2; - }, - calculateBaseWidth : function(){ - return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing); - }, - calculateBarWidth : function(datasetCount){ - //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset - var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing); - - return (baseWidth / datasetCount); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : []; - - this.eachBars(function(bar){ - bar.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activeBars, function(activeBar){ - activeBar.fillColor = activeBar.highlightFill; - activeBar.strokeColor = activeBar.highlightStroke; - }); - this.showTooltip(activeBars); - }); - } - - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.BarClass = Chart.Rectangle.extend({ - strokeWidth : this.options.barStrokeWidth, - showStroke : this.options.barShowStroke, - ctx : this.chart.ctx - }); - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset,datasetIndex){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - bars : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.bars.push(new this.BarClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.strokeColor, - fillColor : dataset.fillColor, - highlightFill : dataset.highlightFill || dataset.fillColor, - highlightStroke : dataset.highlightStroke || dataset.strokeColor - })); - },this); - - },this); - - this.buildScale(data.labels); - - this.BarClass.prototype.base = this.scale.endPoint; - - this.eachBars(function(bar, index, datasetIndex){ - helpers.extend(bar, { - width : this.scale.calculateBarWidth(this.datasets.length), - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y: this.scale.endPoint - }); - bar.save(); - }, this); - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - - this.eachBars(function(bar){ - bar.save(); - }); - this.render(); - }, - eachBars : function(callback){ - helpers.each(this.datasets,function(dataset, datasetIndex){ - helpers.each(dataset.bars, callback, this, datasetIndex); - },this); - }, - getBarsAtEvent : function(e){ - var barsArray = [], - eventPosition = helpers.getRelativePosition(e), - datasetIterator = function(dataset){ - barsArray.push(dataset.bars[barIndex]); - }, - barIndex; - - for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) { - for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) { - if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){ - helpers.each(this.datasets, datasetIterator); - return barsArray; - } - } - } - - return barsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachBars(function(bar){ - values.push(bar.value); - }); - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange: function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - this.scale = new this.ScaleClass(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].bars.push(new this.BarClass({ - value : value, - label : label, - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1), - y: this.scale.endPoint, - width : this.scale.calculateBarWidth(this.datasets.length), - base : this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].strokeColor, - fillColor : this.datasets[datasetIndex].fillColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.bars.shift(); - },this); - this.update(); - }, - reflow : function(){ - helpers.extend(this.BarClass.prototype,{ - y: this.scale.endPoint, - base : this.scale.endPoint - }); - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - this.scale.draw(easingDecimal); - - //Draw all the bars for each dataset - helpers.each(this.datasets,function(dataset,datasetIndex){ - helpers.each(dataset.bars,function(bar,index){ - if (bar.hasValue()){ - bar.base = this.scale.endPoint; - //Transition then draw - bar.transition({ - x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y : this.scale.calculateY(bar.value), - width : this.scale.calculateBarWidth(this.datasets.length) - }, easingDecimal).draw(); - } - },this); - - },this); - } - }); - - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke : true, - - //String - The colour of each segment stroke - segmentStrokeColor : "#fff", - - //Number - The width of each segment stroke - segmentStrokeWidth : 2, - - //The percentage of the chart that we cut out of the middle. - percentageInnerCutout : 50, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect - animationEasing : "easeOutBounce", - - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate : true, - - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>
" - - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "Doughnut", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - - //Declare segments as a static property to prevent inheriting across the Chart type prototype - this.segments = []; - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - - this.SegmentArc = Chart.Arc.extend({ - ctx : this.chart.ctx, - x : this.chart.width/2, - y : this.chart.height/2 - }); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - this.calculateTotal(data); - - helpers.each(data,function(datapoint, index){ - this.addData(datapoint, index, true); - },this); - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - this.segments.splice(index, 0, new this.SegmentArc({ - value : segment.value, - outerRadius : (this.options.animateScale) ? 0 : this.outerRadius, - innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout, - fillColor : segment.color, - highlightColor : segment.highlight || segment.color, - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - startAngle : Math.PI * 1.5, - circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), - label : segment.label - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - calculateCircumference : function(value){ - return (Math.PI*2)*(Math.abs(value) / this.total); - }, - calculateTotal : function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += Math.abs(segment.value); - },this); - }, - update : function(){ - this.calculateTotal(this.segments); - - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor']); - }); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - this.render(); - }, - - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - }); - }, this); - }, - draw : function(easeDecimal){ - var animDecimal = (easeDecimal) ? easeDecimal : 1; - this.clear(); - helpers.each(this.segments,function(segment,index){ - segment.transition({ - circumference : this.calculateCircumference(segment.value), - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - },animDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - segment.draw(); - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length-1){ - this.segments[index+1].startAngle = segment.endAngle; - } - },this); - - } - }); - - Chart.types.Doughnut.extend({ - name : "Pie", - defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0}) - }); - -}).call(this); -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - var defaultConfig = { - - ///Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - Whether the line is curved between points - bezierCurve : true, - - //Number - Tension of the bezier curve between points - bezierCurveTension : 0.4, - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 4, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" - - }; - - - Chart.Type.extend({ - name: "Line", - defaults : defaultConfig, - initialize: function(data){ - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx, - inRange : function(mouseX){ - return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2)); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePoints, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - this.showTooltip(activePoints); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - this.buildScale(data.labels); - - - this.eachPoints(function(point, index){ - helpers.extend(point, { - x: this.scale.calculateX(index), - y: this.scale.endPoint - }); - point.save(); - }, this); - - },this); - - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - this.eachPoints(function(point){ - point.save(); - }); - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - getPointsAtEvent : function(e){ - var pointsArray = [], - eventPosition = helpers.getRelativePosition(e); - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,function(point){ - if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point); - }); - },this); - return pointsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachPoints(function(point){ - values.push(point.value); - }); - - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange : function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - - this.scale = new Chart.Scale(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: this.scale.calculateX(this.scale.valuesCount+1), - y: this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.update(); - }, - reflow : function(){ - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - // Some helper methods for getting the next/prev points - var hasValue = function(item){ - return item.value !== null; - }, - nextPoint = function(point, collection, index){ - return helpers.findNextWhere(collection, hasValue, index) || point; - }, - previousPoint = function(point, collection, index){ - return helpers.findPreviousWhere(collection, hasValue, index) || point; - }; - - this.scale.draw(easingDecimal); - - - helpers.each(this.datasets,function(dataset){ - var pointsWithValues = helpers.where(dataset.points, hasValue); - - //Transition each point first so that the line and point drawing isn't out of sync - //We can use this extra loop to calculate the control points of this dataset also in this loop - - helpers.each(dataset.points, function(point, index){ - if (point.hasValue()){ - point.transition({ - y : this.scale.calculateY(point.value), - x : this.scale.calculateX(index) - }, easingDecimal); - } - },this); - - - // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point - // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed - if (this.options.bezierCurve){ - helpers.each(pointsWithValues, function(point, index){ - var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0; - point.controlPoints = helpers.splineCurve( - previousPoint(point, pointsWithValues, index), - point, - nextPoint(point, pointsWithValues, index), - tension - ); - - // Prevent the bezier going outside of the bounds of the graph - - // Cap puter bezier handles to the upper/lower scale bounds - if (point.controlPoints.outer.y > this.scale.endPoint){ - point.controlPoints.outer.y = this.scale.endPoint; - } - else if (point.controlPoints.outer.y < this.scale.startPoint){ - point.controlPoints.outer.y = this.scale.startPoint; - } - - // Cap inner bezier handles to the upper/lower scale bounds - if (point.controlPoints.inner.y > this.scale.endPoint){ - point.controlPoints.inner.y = this.scale.endPoint; - } - else if (point.controlPoints.inner.y < this.scale.startPoint){ - point.controlPoints.inner.y = this.scale.startPoint; - } - },this); - } - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - - helpers.each(pointsWithValues, function(point, index){ - if (index === 0){ - ctx.moveTo(point.x, point.y); - } - else{ - if(this.options.bezierCurve){ - var previous = previousPoint(point, pointsWithValues, index); - - ctx.bezierCurveTo( - previous.controlPoints.outer.x, - previous.controlPoints.outer.y, - point.controlPoints.inner.x, - point.controlPoints.inner.y, - point.x, - point.y - ); - } - else{ - ctx.lineTo(point.x,point.y); - } - } - }, this); - - ctx.stroke(); - - if (this.options.datasetFill && pointsWithValues.length > 0){ - //Round off the line by going to the base of the chart, back to the start, then fill. - ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint); - ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint); - ctx.fillStyle = dataset.fillColor; - ctx.closePath(); - ctx.fill(); - } - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(pointsWithValues,function(point){ - point.draw(); - }); - },this); - } - }); - - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Show a backdrop to the scale label - scaleShowLabelBackdrop : true, - - //String - The colour of the label backdrop - scaleBackdropColor : "rgba(255,255,255,0.75)", - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //Number - The backdrop padding above & below the label in pixels - scaleBackdropPaddingY : 2, - - //Number - The backdrop padding to the side of the label in pixels - scaleBackdropPaddingX : 2, - - //Boolean - Show line for each value in the scale - scaleShowLine : true, - - //Boolean - Stroke a line around each segment in the chart - segmentShowStroke : true, - - //String - The colour of the stroke on each segement. - segmentStrokeColor : "#fff", - - //Number - The width of the stroke value in pixels - segmentStrokeWidth : 2, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect. - animationEasing : "easeOutBounce", - - //Boolean - Whether to animate the rotation of the chart - animateRotate : true, - - //Boolean - Whether to animate scaling the chart from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>
" - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "PolarArea", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - this.segments = []; - //Declare segment class as a chart instance specific class, so it can share props for this instance - this.SegmentArc = Chart.Arc.extend({ - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - ctx : this.chart.ctx, - innerRadius : 0, - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - lineArc: true, - width: this.chart.width, - height: this.chart.height, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - valuesCount: data.length - }); - - this.updateScaleRange(data); - - this.scale.update(); - - helpers.each(data,function(segment,index){ - this.addData(segment,index,true); - },this); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - - this.segments.splice(index, 0, new this.SegmentArc({ - fillColor: segment.color, - highlightColor: segment.highlight || segment.color, - label: segment.label, - value: segment.value, - outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value), - circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(), - startAngle: Math.PI * 1.5 - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - calculateTotal: function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += segment.value; - },this); - this.scale.valuesCount = this.segments.length; - }, - updateScaleRange: function(datapoints){ - var valuesArray = []; - helpers.each(datapoints,function(segment){ - valuesArray.push(segment.value); - }); - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes, - { - size: helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - } - ); - - }, - update : function(){ - this.calculateTotal(this.segments); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - - this.reflow(); - this.render(); - }, - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.updateScaleRange(this.segments); - this.scale.update(); - - helpers.extend(this.scale,{ - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.scale.calculateCenterOffset(segment.value) - }); - }, this); - - }, - draw : function(ease){ - var easingDecimal = ease || 1; - //Clear & draw the canvas - this.clear(); - helpers.each(this.segments,function(segment, index){ - segment.transition({ - circumference : this.scale.getCircumference(), - outerRadius : this.scale.calculateCenterOffset(segment.value) - },easingDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - // If we've removed the first segment we need to set the first one to - // start at the top. - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length - 1){ - this.segments[index+1].startAngle = segment.endAngle; - } - segment.draw(); - }, this); - this.scale.draw(); - } - }); - -}).call(this); -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - - Chart.Type.extend({ - name: "Radar", - defaults:{ - //Boolean - Whether to show lines for each scale point - scaleShowLine : true, - - //Boolean - Whether we show the angle lines out of the radar - angleShowLineOut : true, - - //Boolean - Whether to show labels on the scale - scaleShowLabels : false, - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //String - Colour of the angle line - angleLineColor : "rgba(0,0,0,.1)", - - //Number - Pixel width of the angle line - angleLineWidth : 1, - - //String - Point label font declaration - pointLabelFontFamily : "'Arial'", - - //String - Point label font weight - pointLabelFontStyle : "normal", - - //Number - Point label font size in pixels - pointLabelFontSize : 10, - - //String - Point label font colour - pointLabelFontColor : "#666", - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 3, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" - - }, - - initialize: function(data){ - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx - }); - - this.datasets = []; - - this.buildScale(data); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePointsCollection, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - - this.showTooltip(activePointsCollection); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label: dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - var pointPosition; - if (!this.scale.animation){ - pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint)); - } - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - x: (this.options.animation) ? this.scale.xCenter : pointPosition.x, - y: (this.options.animation) ? this.scale.yCenter : pointPosition.y, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - },this); - - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - - getPointsAtEvent : function(evt){ - var mousePosition = helpers.getRelativePosition(evt), - fromCenter = helpers.getAngleFromPoint({ - x: this.scale.xCenter, - y: this.scale.yCenter - }, mousePosition); - - var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount, - pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex), - activePointsCollection = []; - - // If we're at the top, make the pointIndex 0 to get the first of the array. - if (pointIndex >= this.scale.valuesCount || pointIndex < 0){ - pointIndex = 0; - } - - if (fromCenter.distance <= this.scale.drawingArea){ - helpers.each(this.datasets, function(dataset){ - activePointsCollection.push(dataset.points[pointIndex]); - }); - } - - return activePointsCollection; - }, - - buildScale : function(data){ - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - angleLineColor : this.options.angleLineColor, - angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0, - // Point labels at the edge of each line - pointLabelFontColor : this.options.pointLabelFontColor, - pointLabelFontSize : this.options.pointLabelFontSize, - pointLabelFontFamily : this.options.pointLabelFontFamily, - pointLabelFontStyle : this.options.pointLabelFontStyle, - height : this.chart.height, - width: this.chart.width, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - labels: data.labels, - valuesCount: data.datasets[0].data.length - }); - - this.scale.setScaleSize(); - this.updateScaleRange(data.datasets); - this.scale.buildYLabels(); - }, - updateScaleRange: function(datasets){ - var valuesArray = (function(){ - var totalDataArray = []; - helpers.each(datasets,function(dataset){ - if (dataset.data){ - totalDataArray = totalDataArray.concat(dataset.data); - } - else { - helpers.each(dataset.points, function(point){ - totalDataArray.push(point.value); - }); - } - }); - return totalDataArray; - })(); - - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes - ); - - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - this.scale.valuesCount++; - helpers.each(valuesArray,function(value,datasetIndex){ - var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value)); - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: pointPosition.x, - y: pointPosition.y, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.labels.push(label); - - this.reflow(); - - this.update(); - }, - removeData : function(){ - this.scale.valuesCount--; - this.scale.labels.shift(); - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.reflow(); - this.update(); - }, - update : function(){ - this.eachPoints(function(point){ - point.save(); - }); - this.reflow(); - this.render(); - }, - reflow: function(){ - helpers.extend(this.scale, { - width : this.chart.width, - height: this.chart.height, - size : helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - this.updateScaleRange(this.datasets); - this.scale.setScaleSize(); - this.scale.buildYLabels(); - }, - draw : function(ease){ - var easeDecimal = ease || 1, - ctx = this.chart.ctx; - this.clear(); - this.scale.draw(); - - helpers.each(this.datasets,function(dataset){ - - //Transition each point first so that the line and point drawing isn't out of sync - helpers.each(dataset.points,function(point,index){ - if (point.hasValue()){ - point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal); - } - },this); - - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - helpers.each(dataset.points,function(point,index){ - if (index === 0){ - ctx.moveTo(point.x,point.y); - } - else{ - ctx.lineTo(point.x,point.y); - } - },this); - ctx.closePath(); - ctx.stroke(); - - ctx.fillStyle = dataset.fillColor; - ctx.fill(); - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(dataset.points,function(point){ - if (point.hasValue()){ - point.draw(); - } - }); - - },this); - - } - - }); - - - - - -}).call(this); \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/Chart.min.js b/web/backend/resources/bower_components/chart.js/Chart.min.js deleted file mode 100644 index 3a0a2c873..000000000 --- a/web/backend/resources/bower_components/chart.js/Chart.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * Chart.js - * http://chartjs.org/ - * Version: 1.0.2 - * - * Copyright 2015 Nick Downie - * Released under the MIT license - * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md - */ -(function(){"use strict";var t=this,i=t.Chart,e=function(t){this.canvas=t.canvas,this.ctx=t;var i=function(t,i){return t["offset"+i]?t["offset"+i]:document.defaultView.getComputedStyle(t).getPropertyValue(i)},e=this.width=i(t.canvas,"Width"),n=this.height=i(t.canvas,"Height");t.canvas.width=e,t.canvas.height=n;var e=this.width=t.canvas.width,n=this.height=t.canvas.height;return this.aspectRatio=this.width/this.height,s.retinaScale(this),this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",multiTooltipKeyBackground:"#fff",onAnimationProgress:function(){},onAnimationComplete:function(){}}},e.types={};var s=e.helpers={},n=s.each=function(t,i,e){var s=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var n;for(n=0;n=0;s--){var n=t[s];if(i(n))return n}},s.inherits=function(t){var i=this,e=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return i.apply(this,arguments)},s=function(){this.constructor=e};return s.prototype=i.prototype,e.prototype=new s,e.extend=r,t&&a(e.prototype,t),e.__super__=i.prototype,e}),c=s.noop=function(){},u=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),d=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},p=s.amd="function"==typeof define&&define.amd,f=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},g=s.max=function(t){return Math.max.apply(Math,t)},m=s.min=function(t){return Math.min.apply(Math,t)},v=(s.cap=function(t,i,e){if(f(i)){if(t>i)return i}else if(f(e)&&e>t)return e;return t},s.getDecimalPlaces=function(t){return t%1!==0&&f(t)?t.toString().split(".")[1].length:0}),S=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,i){var e=i.x-t.x,s=i.y-t.y,n=Math.sqrt(e*e+s*s),o=2*Math.PI+Math.atan2(s,e);return 0>e&&0>s&&(o+=2*Math.PI),{angle:o,distance:n}},s.aliasPixel=function(t){return t%2===0?0:.5}),y=(s.splineCurve=function(t,i,e,s){var n=Math.sqrt(Math.pow(i.x-t.x,2)+Math.pow(i.y-t.y,2)),o=Math.sqrt(Math.pow(e.x-i.x,2)+Math.pow(e.y-i.y,2)),a=s*n/(n+o),h=s*o/(n+o);return{inner:{x:i.x-a*(e.x-t.x),y:i.y-a*(e.y-t.y)},outer:{x:i.x+h*(e.x-t.x),y:i.y+h*(e.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),C=(s.calculateScaleRange=function(t,i,e,s,n){var o=2,a=Math.floor(i/(1.5*e)),h=o>=a,l=g(t),r=m(t);l===r&&(l+=.5,r>=.5&&!s?r-=.5:l+=.5);for(var c=Math.abs(l-r),u=y(c),d=Math.ceil(l/(1*Math.pow(10,u)))*Math.pow(10,u),p=s?0:Math.floor(r/(1*Math.pow(10,u)))*Math.pow(10,u),f=d-p,v=Math.pow(10,u),S=Math.round(f/v);(S>a||a>2*S)&&!h;)if(S>a)v*=2,S=Math.round(f/v),S%1!==0&&(h=!0);else if(n&&u>=0){if(v/2%1!==0)break;v/=2,S=Math.round(f/v)}else v/=2,S=Math.round(f/v);return h&&(S=o,v=f/S),{steps:S,stepValue:v,min:p,max:p+S*v}},s.template=function(t,i){function e(t,i){var e=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):s[t]=s[t];return i?e(i):e}if(t instanceof Function)return t(i);var s={};return e(t,i)}),w=(s.generateLabels=function(t,i,e,s){var o=new Array(i);return labelTemplateString&&n(o,function(i,n){o[n]=C(t,{value:e+s*(n+1)})}),o},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-0.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-0.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-0.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-0.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var i=1.70158,e=0,s=1;return 0===t?0:1==(t/=1)?1:(e||(e=.3),st?-.5*s*Math.pow(2,10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e):s*Math.pow(2,-10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e)*.5+1)},easeInBack:function(t){var i=1.70158;return 1*(t/=1)*t*((i+1)*t-i)},easeOutBack:function(t){var i=1.70158;return 1*((t=t/1-1)*t*((i+1)*t+i)+1)},easeInOutBack:function(t){var i=1.70158;return(t/=.5)<1?.5*t*t*(((i*=1.525)+1)*t-i):.5*((t-=2)*t*(((i*=1.525)+1)*t+i)+2)},easeInBounce:function(t){return 1-w.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?7.5625*t*t:2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*w.easeInBounce(2*t):.5*w.easeOutBounce(2*t-1)+.5}}),b=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),P=s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),L=(s.animationLoop=function(t,i,e,s,n,o){var a=0,h=w[e]||w.linear,l=function(){a++;var e=a/i,r=h(e);t.call(o,r,e,a),s.call(o,r,e),i>a?o.animationFrame=b(l):n.apply(o)};b(l)},s.getRelativePosition=function(t){var i,e,s=t.originalEvent||t,n=t.currentTarget||t.srcElement,o=n.getBoundingClientRect();return s.touches?(i=s.touches[0].clientX-o.left,e=s.touches[0].clientY-o.top):(i=s.clientX-o.left,e=s.clientY-o.top),{x:i,y:e}},s.addEvent=function(t,i,e){t.addEventListener?t.addEventListener(i,e):t.attachEvent?t.attachEvent("on"+i,e):t["on"+i]=e}),k=s.removeEvent=function(t,i,e){t.removeEventListener?t.removeEventListener(i,e,!1):t.detachEvent?t.detachEvent("on"+i,e):t["on"+i]=c},F=(s.bindEvents=function(t,i,e){t.events||(t.events={}),n(i,function(i){t.events[i]=function(){e.apply(t,arguments)},L(t.chart.canvas,i,t.events[i])})},s.unbindEvents=function(t,i){n(i,function(i,e){k(t.chart.canvas,e,i)})}),R=s.getMaximumWidth=function(t){var i=t.parentNode;return i.clientWidth},T=s.getMaximumHeight=function(t){var i=t.parentNode;return i.clientHeight},A=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var i=t.ctx,e=t.canvas.width,s=t.canvas.height;window.devicePixelRatio&&(i.canvas.style.width=e+"px",i.canvas.style.height=s+"px",i.canvas.height=s*window.devicePixelRatio,i.canvas.width=e*window.devicePixelRatio,i.scale(window.devicePixelRatio,window.devicePixelRatio))}),M=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},W=s.fontString=function(t,i,e){return i+" "+t+"px "+e},z=s.longestText=function(t,i,e){t.font=i;var s=0;return n(e,function(i){var e=t.measureText(i).width;s=e>s?e:s}),s},B=s.drawRoundedRectangle=function(t,i,e,s,n,o){t.beginPath(),t.moveTo(i+o,e),t.lineTo(i+s-o,e),t.quadraticCurveTo(i+s,e,i+s,e+o),t.lineTo(i+s,e+n-o),t.quadraticCurveTo(i+s,e+n,i+s-o,e+n),t.lineTo(i+o,e+n),t.quadraticCurveTo(i,e+n,i,e+n-o),t.lineTo(i,e+o),t.quadraticCurveTo(i,e,i+o,e),t.closePath()};e.instances={},e.Type=function(t,i,s){this.options=i,this.chart=s,this.id=u(),e.instances[this.id]=this,i.responsive&&this.resize(),this.initialize.call(this,t)},a(e.Type.prototype,{initialize:function(){return this},clear:function(){return M(this.chart),this},stop:function(){return P(this.animationFrame),this},resize:function(t){this.stop();var i=this.chart.canvas,e=R(this.chart.canvas),s=this.options.maintainAspectRatio?e/this.chart.aspectRatio:T(this.chart.canvas);return i.width=this.chart.width=e,i.height=this.chart.height=s,A(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:c,render:function(t){return t&&this.reflow(),this.options.animation&&!t?s.animationLoop(this.draw,this.options.animationSteps,this.options.animationEasing,this.options.onAnimationProgress,this.options.onAnimationComplete,this):(this.draw(),this.options.onAnimationComplete.call(this)),this},generateLegend:function(){return C(this.options.legendTemplate,this)},destroy:function(){this.clear(),F(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete e.instances[this.id]},showTooltip:function(t,i){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var o=function(t){var i=!1;return t.length!==this.activeElements.length?i=!0:(n(t,function(t,e){t!==this.activeElements[e]&&(i=!0)},this),i)}.call(this,t);if(o||i){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var a,h,r=this.datasets.length-1;r>=0&&(a=this.datasets[r].points||this.datasets[r].bars||this.datasets[r].segments,h=l(a,t[0]),-1===h);r--);var c=[],u=[],d=function(){var t,i,e,n,o,a=[],l=[],r=[];return s.each(this.datasets,function(i){t=i.points||i.bars||i.segments,t[h]&&t[h].hasValue()&&a.push(t[h])}),s.each(a,function(t){l.push(t.x),r.push(t.y),c.push(s.template(this.options.multiTooltipTemplate,t)),u.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),o=m(r),e=g(r),n=m(l),i=g(l),{x:n>this.chart.width/2?n:i,y:(o+e)/2}}.call(this,h);new e.MultiTooltip({x:d.x,y:d.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:c,legendColors:u,legendColorBackground:this.options.multiTooltipKeyBackground,title:t[0].label,chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else n(t,function(t){var i=t.tooltipPosition();new e.Tooltip({x:Math.round(i.x),y:Math.round(i.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:C(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),e.Type.extend=function(t){var i=this,s=function(){return i.apply(this,arguments)};if(s.prototype=o(i.prototype),a(s.prototype,t),s.extend=e.Type.extend,t.name||i.prototype.name){var n=t.name||i.prototype.name,l=e.defaults[i.prototype.name]?o(e.defaults[i.prototype.name]):{};e.defaults[n]=a(l,t.defaults),e.types[n]=s,e.prototype[n]=function(t,i){var o=h(e.defaults.global,e.defaults[n],i||{});return new s(t,o,this)}}else d("Name not provided for this chart, so it hasn't been registered");return i},e.Element=function(t){a(this,t),this.initialize.apply(this,arguments),this.save()},a(e.Element.prototype,{initialize:function(){},restore:function(t){return t?n(t,function(t){this[t]=this._saved[t]},this):a(this,this._saved),this},save:function(){return this._saved=o(this),delete this._saved._saved,this},update:function(t){return n(t,function(t,i){this._saved[i]=this[i],this[i]=t},this),this},transition:function(t,i){return n(t,function(t,e){this[e]=(t-this._saved[e])*i+this._saved[e]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return f(this.value)}}),e.Element.extend=r,e.Point=e.Element.extend({display:!0,inRange:function(t,i){var e=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(i-this.y,2)=this.startAngle&&e.angle<=this.endAngle,o=e.distance>=this.innerRadius&&e.distance<=this.outerRadius;return n&&o},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,i=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*i,y:this.y+Math.sin(t)*i}},draw:function(t){var i=this.ctx;i.beginPath(),i.arc(this.x,this.y,this.outerRadius,this.startAngle,this.endAngle),i.arc(this.x,this.y,this.innerRadius,this.endAngle,this.startAngle,!0),i.closePath(),i.strokeStyle=this.strokeColor,i.lineWidth=this.strokeWidth,i.fillStyle=this.fillColor,i.fill(),i.lineJoin="bevel",this.showStroke&&i.stroke()}}),e.Rectangle=e.Element.extend({draw:function(){var t=this.ctx,i=this.width/2,e=this.x-i,s=this.x+i,n=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(e+=o,s-=o,n+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(e,this.base),t.lineTo(e,n),t.lineTo(s,n),t.lineTo(s,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,i){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&i>=this.y&&i<=this.base}}),e.Tooltip=e.Element.extend({draw:function(){var t=this.chart.ctx;t.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var i=this.caretPadding=2,e=t.measureText(this.text).width+2*this.xPadding,s=this.fontSize+2*this.yPadding,n=s+this.caretHeight+i;this.x+e/2>this.chart.width?this.xAlign="left":this.x-e/2<0&&(this.xAlign="right"),this.y-n<0&&(this.yAlign="below");var o=this.x-e/2,a=this.y-n;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-i),t.lineTo(this.x+this.caretHeight,this.y-(i+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(i+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+i+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+i),t.lineTo(this.x+this.caretHeight,this.y+i+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+i+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-e+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}B(t,o,a,e,s,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+e/2,a+s/2)}}}),e.MultiTooltip=e.Element.extend({initialize:function(){this.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=W(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+1.5*this.titleFontSize,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,i=z(this.ctx,this.font,this.labels)+this.fontSize+3,e=g([i,t]);this.width=e+2*this.xPadding;var s=this.height/2;this.y-s<0?this.y=s:this.y+s>this.chart.height&&(this.y=this.chart.height-s),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var i=this.y-this.height/2+this.yPadding,e=t-1;return 0===t?i+this.titleFontSize/2:i+(1.5*this.fontSize*e+this.fontSize/2)+1.5*this.titleFontSize},draw:function(){if(this.custom)this.custom(this);else{B(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(i,e){t.fillStyle=this.textColor,t.fillText(i,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(e+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[e].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?z(this.ctx,this.font,this.yLabels):0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,i=this.endPoint-this.startPoint;for(this.calculateYRange(i),this.buildYLabels(),this.calculateXLabelRotation();i>this.endPoint-this.startPoint;)i=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(i),this.buildYLabels(),tthis.yLabelWidth+10?e/2:this.yLabelWidth+10,this.xLabelRotation=0,this.display){var n,o=z(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)n=Math.cos(S(this.xLabelRotation)),t=n*e,i=n*s,t+this.fontSize/2>this.yLabelWidth+8&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=n*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(S(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:c,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var i=this.drawingArea()/(this.min-this.max);return this.endPoint-i*(t-this.min)},calculateX:function(t){var i=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),e=i/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1),s=e*t+this.xScalePaddingLeft;return this.offsetGridLines&&(s+=e/2),Math.round(s)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,i=(this.endPoint-this.startPoint)/this.steps,e=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,n(this.yLabels,function(n,o){var a=this.endPoint-i*o,h=Math.round(a),l=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(n,e-10,a),0!==o||l||(l=!0),l&&t.beginPath(),o>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),h+=s.aliasPixel(t.lineWidth),l&&(t.moveTo(e,h),t.lineTo(this.width,h),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(e-5,h),t.lineTo(e,h),t.stroke(),t.closePath()},this),n(this.xLabels,function(i,e){var s=this.calculateX(e)+x(this.lineWidth),n=this.calculateX(e-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==e||a||(a=!0),a&&t.beginPath(),e>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(n,this.endPoint),t.lineTo(n,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n,this.endPoint),t.lineTo(n,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(s,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*S(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(i,0,0),t.restore()},this))}}),e.RadialScale=e.Element.extend({initialize:function(){this.size=m([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var i=this.drawingArea/(this.max-this.min);return(t-this.min)*i},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,i,e,s,n,o,a,h,l,r,c,u,d=m([this.height/2-this.pointLabelFontSize-5,this.width/2]),p=this.width,g=0;for(this.ctx.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),i=0;ip&&(p=t.x+s,n=i),t.x-sp&&(p=t.x+e,n=i):i>this.valuesCount/2&&t.x-e0){var s,n=e*(this.drawingArea/this.steps),o=this.yCenter-n;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,n,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;i--){if(this.angleLineWidth>0){var e=this.getPointPosition(i,this.calculateCenterOffset(this.max));t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(e.x,e.y),t.stroke(),t.closePath()}var s=this.getPointPosition(i,this.calculateCenterOffset(this.max)+5);t.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var o=this.labels.length,a=this.labels.length/2,h=a/2,l=h>i||i>o-h,r=i===h||i===o-h;t.textAlign=0===i?"center":i===a?"center":a>i?"left":"right",t.textBaseline=r?"middle":l?"bottom":"top",t.fillText(this.labels[i],s.x,s.y)}}}}}),s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){n(e.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),p?define(function(){return e}):"object"==typeof module&&module.exports&&(module.exports=e),t.Chart=e,e.noConflict=function(){return t.Chart=i,e}}).call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'
    <% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
'};i.Type.extend({name:"Bar",defaults:s,initialize:function(t){var s=this.options;this.ScaleClass=i.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,i,e){var n=this.calculateBaseWidth(),o=this.calculateX(e)-n/2,a=this.calculateBarWidth(t);return o+a*i+i*s.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*s.barValueSpacing},calculateBarWidth:function(t){var i=this.calculateBaseWidth()-(t-1)*s.barDatasetSpacing;return i/t}}),this.datasets=[],this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),this.BarClass=i.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,bars:[]};this.datasets.push(s),e.each(i.data,function(e,n){s.bars.push(new this.BarClass({value:e,label:t.labels[n],datasetLabel:i.label,strokeColor:i.strokeColor,fillColor:i.fillColor,highlightFill:i.highlightFill||i.fillColor,highlightStroke:i.highlightStroke||i.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,i,s){e.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,s,i),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),e.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){e.each(this.datasets,function(i,s){e.each(i.bars,t,this,s)},this)},getBarsAtEvent:function(t){for(var i,s=[],n=e.getRelativePosition(t),o=function(t){s.push(t.bars[i])},a=0;a<% for (var i=0; i
  • <%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Doughnut",defaults:s,initialize:function(t){this.segments=[],this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=i.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.calculateTotal(t),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),e||(this.reflow(),this.update())},calculateCircumference:function(t){return 2*Math.PI*(Math.abs(t)/this.total)},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=Math.abs(t.value)},this)},update:function(){this.calculateTotal(this.segments),e.each(this.activeElements,function(t){t.restore(["fillColor"])}),e.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,e.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var i=t?t:1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},i),t.endAngle=t.startAngle+t.circumference,t.draw(),0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Line",defaults:s,initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ithis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(s.lineTo(h[h.length-1].x,this.scale.endPoint),s.lineTo(h[0].x,this.scale.endPoint),s.fillStyle=t.fillColor,s.closePath(),s.fill()),e.each(h,function(t){t.draw()})},this)}})}.call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'
      <% for (var i=0; i
    • <%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    '};i.Type.extend({name:"PolarArea",defaults:s,initialize:function(t){this.segments=[],this.SegmentArc=i.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),e||(this.reflow(),this.update())},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var i=[];e.each(t,function(t){i.push(t.value)});var s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s,{size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),e.each(this.segments,function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),e.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),e.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var i=t||1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},i),t.endAngle=t.startAngle+t.circumference,0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'},initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,pointColor:i.pointColor,pointStrokeColor:i.pointStrokeColor,points:[]};this.datasets.push(s),e.each(i.data,function(e,n){var o;this.scale.animation||(o=this.scale.getPointPosition(n,this.scale.calculateCenterOffset(e))),s.points.push(new this.PointClass({value:e,label:t.labels[n],datasetLabel:i.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:i.pointStrokeColor,fillColor:i.pointColor,highlightFill:i.pointHighlightFill||i.pointColor,highlightStroke:i.pointHighlightStroke||i.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){e.each(this.datasets,function(i){e.each(i.points,t,this)},this)},getPointsAtEvent:function(t){var i=e.getRelativePosition(t),s=e.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},i),n=2*Math.PI/this.scale.valuesCount,o=Math.round((s.angle-1.5*Math.PI)/n),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),s.distance<=this.scale.drawingArea&&e.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var i=function(){var i=[];return e.each(t,function(t){t.data?i=i.concat(t.data):e.each(t.points,function(t){i.push(t.value)})}),i}(),s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s)},addData:function(t,i){this.scale.valuesCount++,e.each(t,function(t,e){var s=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[e].points.push(new this.PointClass({value:t,label:i,x:s.x,y:s.y,strokeColor:this.datasets[e].pointStrokeColor,fillColor:this.datasets[e].pointColor}))},this),this.scale.labels.push(i),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),e.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var i=t||1,s=this.chart.ctx;this.clear(),this.scale.draw(),e.each(this.datasets,function(t){e.each(t.points,function(t,e){t.hasValue()&&t.transition(this.scale.getPointPosition(e,this.scale.calculateCenterOffset(t.value)),i)},this),s.lineWidth=this.options.datasetStrokeWidth,s.strokeStyle=t.strokeColor,s.beginPath(),e.each(t.points,function(t,i){0===i?s.moveTo(t.x,t.y):s.lineTo(t.x,t.y)},this),s.closePath(),s.stroke(),s.fillStyle=t.fillColor,s.fill(),e.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this); \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/LICENSE.md b/web/backend/resources/bower_components/chart.js/LICENSE.md deleted file mode 100644 index e10bc0ff1..000000000 --- a/web/backend/resources/bower_components/chart.js/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2013-2015 Nick Downie - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/web/backend/resources/bower_components/chart.js/README.md b/web/backend/resources/bower_components/chart.js/README.md deleted file mode 100644 index 7c4fa8bf6..000000000 --- a/web/backend/resources/bower_components/chart.js/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Chart.js - -[![Build Status](https://travis-ci.org/nnnick/Chart.js.svg?branch=master)](https://travis-ci.org/nnnick/Chart.js) [![Code Climate](https://codeclimate.com/github/nnnick/Chart.js/badges/gpa.svg)](https://codeclimate.com/github/nnnick/Chart.js) - - -*Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org) - -## Documentation - -You can find documentation at [chartjs.org/docs](http://www.chartjs.org/docs/). The markdown files that build the site are available under `/docs`. Please note - in some of the json examples of configuration you might notice some liquid tags - this is just for the generating the site html, please disregard. - -## Bugs, issues and contributing - -Before submitting an issue or a pull request to the project, please take a moment to look over the [contributing guidelines](https://github.com/nnnick/Chart.js/blob/master/CONTRIBUTING.md) first. - -For support using Chart.js, please post questions with the [`chartjs` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/chartjs). - -## License - -Chart.js is available under the [MIT license](http://opensource.org/licenses/MIT). diff --git a/web/backend/resources/bower_components/chart.js/bower.json b/web/backend/resources/bower_components/chart.js/bower.json deleted file mode 100644 index f26f81eef..000000000 --- a/web/backend/resources/bower_components/chart.js/bower.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Chart.js", - "version": "1.0.2", - "description": "Simple HTML5 Charts using the canvas element", - "homepage": "https://github.com/nnnick/Chart.js", - "author": "nnnick", - "main": [ - "Chart.js" - ], - "dependencies": {} -} \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/docs/00-Getting-Started.md b/web/backend/resources/bower_components/chart.js/docs/00-Getting-Started.md deleted file mode 100644 index 273b10f2e..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/00-Getting-Started.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: Getting started -anchor: getting-started ---- - -###Include Chart.js - -First we need to include the Chart.js library on the page. The library occupies a global variable of `Chart`. - -```html - -``` - -Alternatively, if you're using an AMD loader for JavaScript modules, that is also supported in the Chart.js core. Please note: the library will still occupy a global variable of `Chart`, even if it detects `define` and `define.amd`. If this is a problem, you can call `noConflict` to restore the global Chart variable to it's previous owner. - -```javascript -// Using requirejs -require(['path/to/Chartjs'], function(Chart){ - // Use Chart.js as normal here. - - // Chart.noConflict restores the Chart global variable to it's previous owner - // The function returns what was previously Chart, allowing you to reassign. - var Chartjs = Chart.noConflict(); - -}); -``` - -You can also grab Chart.js using bower: - -```bash -bower install Chart.js --save -``` - -###Creating a chart - -To create a chart, we need to instantiate the `Chart` class. To do this, we need to pass in the 2d context of where we want to draw the chart. Here's an example. - -```html - -``` - -```javascript -// Get the context of the canvas element we want to select -var ctx = document.getElementById("myChart").getContext("2d"); -var myNewChart = new Chart(ctx).PolarArea(data); -``` - -We can also get the context of our canvas with jQuery. To do this, we need to get the DOM node out of the jQuery collection, and call the `getContext("2d")` method on that. - -```javascript -// Get context with jQuery - using jQuery's .get() method. -var ctx = $("#myChart").get(0).getContext("2d"); -// This will get the first returned node in the jQuery collection. -var myNewChart = new Chart(ctx); -``` - -After we've instantiated the Chart class on the canvas we want to draw on, Chart.js will handle the scaling for retina displays. - -With the Chart class set up, we can go on to create one of the charts Chart.js has available. In the example below, we would be drawing a Polar area chart. - -```javascript -new Chart(ctx).PolarArea(data, options); -``` - -We call a method of the name of the chart we want to create. We pass in the data for that chart type, and the options for that chart as parameters. Chart.js will merge the global defaults with chart type specific defaults, then merge any options passed in as a second argument after data. - -###Global chart configuration - -This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow for changing options globally across chart types, avoiding the need to specify options for each instance, or the default for a particular chart type. - -```javascript -Chart.defaults.global = { - // Boolean - Whether to animate the chart - animation: true, - - // Number - Number of animation steps - animationSteps: 60, - - // String - Animation easing effect - animationEasing: "easeOutQuart", - - // Boolean - If we should show the scale at all - showScale: true, - - // Boolean - If we want to override with a hard coded scale - scaleOverride: false, - - // ** Required if scaleOverride is true ** - // Number - The number of steps in a hard coded scale - scaleSteps: null, - // Number - The value jump in the hard coded scale - scaleStepWidth: null, - // Number - The scale starting value - scaleStartValue: null, - - // String - Colour of the scale line - scaleLineColor: "rgba(0,0,0,.1)", - - // Number - Pixel width of the scale line - scaleLineWidth: 1, - - // Boolean - Whether to show labels on the scale - scaleShowLabels: true, - - // Interpolated JS string - can access value - scaleLabel: "<%=value%>", - - // Boolean - Whether the scale should stick to integers, not floats even if drawing space is there - scaleIntegersOnly: true, - - // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero: false, - - // String - Scale label font declaration for the scale label - scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Scale label font size in pixels - scaleFontSize: 12, - - // String - Scale label font weight style - scaleFontStyle: "normal", - - // String - Scale label font colour - scaleFontColor: "#666", - - // Boolean - whether or not the chart should be responsive and resize when the browser does. - responsive: false, - - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: true, - - // Boolean - Determines whether to draw tooltips on the canvas or not - showTooltips: true, - - // Function - Determines whether to execute the customTooltips function instead of drawing the built in tooltips (See [Advanced - External Tooltips](#advanced-usage-custom-tooltips)) - customTooltips: false, - - // Array - Array of string names to attach tooltip events - tooltipEvents: ["mousemove", "touchstart", "touchmove"], - - // String - Tooltip background colour - tooltipFillColor: "rgba(0,0,0,0.8)", - - // String - Tooltip label font declaration for the scale label - tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip label font size in pixels - tooltipFontSize: 14, - - // String - Tooltip font weight style - tooltipFontStyle: "normal", - - // String - Tooltip label font colour - tooltipFontColor: "#fff", - - // String - Tooltip title font declaration for the scale label - tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip title font size in pixels - tooltipTitleFontSize: 14, - - // String - Tooltip title font weight style - tooltipTitleFontStyle: "bold", - - // String - Tooltip title font colour - tooltipTitleFontColor: "#fff", - - // Number - pixel width of padding around tooltip text - tooltipYPadding: 6, - - // Number - pixel width of padding around tooltip text - tooltipXPadding: 6, - - // Number - Size of the caret on the tooltip - tooltipCaretSize: 8, - - // Number - Pixel radius of the tooltip border - tooltipCornerRadius: 6, - - // Number - Pixel offset from point x to tooltip edge - tooltipXOffset: 10, - {% raw %} - // String - Template string for single tooltips - tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>", - {% endraw %} - // String - Template string for multiple tooltips - multiTooltipTemplate: "<%= value %>", - - // Function - Will fire on animation progression. - onAnimationProgress: function(){}, - - // Function - Will fire on animation completion. - onAnimationComplete: function(){} -} -``` - -If for example, you wanted all charts created to be responsive, and resize when the browser window does, the following setting can be changed: - -```javascript -Chart.defaults.global.responsive = true; -``` - -Now, every time we create a chart, `options.responsive` will be `true`. diff --git a/web/backend/resources/bower_components/chart.js/docs/01-Line-Chart.md b/web/backend/resources/bower_components/chart.js/docs/01-Line-Chart.md deleted file mode 100644 index 1a5ae84c4..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/01-Line-Chart.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Line Chart -anchor: line-chart ---- -###Introduction -A line chart is a way of plotting data points on a line. - -Often, it is used to show trend data, and the comparison of two data sets. - -
    - -
    - -###Example usage -```javascript -var myLineChart = new Chart(ctx).Line(data, options); -``` -###Data structure - -```javascript -var data = { - labels: ["January", "February", "March", "April", "May", "June", "July"], - datasets: [ - { - label: "My First dataset", - fillColor: "rgba(220,220,220,0.2)", - strokeColor: "rgba(220,220,220,1)", - pointColor: "rgba(220,220,220,1)", - pointStrokeColor: "#fff", - pointHighlightFill: "#fff", - pointHighlightStroke: "rgba(220,220,220,1)", - data: [65, 59, 80, 81, 56, 55, 40] - }, - { - label: "My Second dataset", - fillColor: "rgba(151,187,205,0.2)", - strokeColor: "rgba(151,187,205,1)", - pointColor: "rgba(151,187,205,1)", - pointStrokeColor: "#fff", - pointHighlightFill: "#fff", - pointHighlightStroke: "rgba(151,187,205,1)", - data: [28, 48, 40, 19, 86, 27, 90] - } - ] -}; -``` - -The line chart requires an array of labels for each of the data points. This is shown on the X axis. -The data for line charts is broken up into an array of datasets. Each dataset has a colour for the fill, a colour for the line and colours for the points and strokes of the points. These colours are strings just like CSS. You can use RGBA, RGB, HEX or HSL notation. - -The label key on each dataset is optional, and can be used when generating a scale for the chart. - -### Chart options - -These are the customisation options specific to Line charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart. - -```javascript -{ - - ///Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - Whether the line is curved between points - bezierCurve : true, - - //Number - Tension of the bezier curve between points - bezierCurveTension : 0.4, - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 4, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - {% raw %} - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - {% endraw %} -}; -``` - -You can override these for your `Chart` instance by passing a second argument into the `Line` method as an object with the keys you want to override. - -For example, we could have a line chart without bezier curves between points by doing the following: - -```javascript -new Chart(ctx).Line(data, { - bezierCurve: false -}); -// This will create a chart with all of the default options, merged from the global config, -// and the Line chart defaults, but this particular instance will have `bezierCurve` set to false. -``` - -We can also change these defaults values for each Line type that is created, this object is available at `Chart.defaults.Line`. - - -### Prototype methods - -#### .getPointsAtEvent( event ) - -Calling `getPointsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event. - -```javascript -canvas.onclick = function(evt){ - var activePoints = myLineChart.getPointsAtEvent(evt); - // => activePoints is an array of points on the canvas that are at the same position as the click event. -}; -``` - -This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application. - -#### .update( ) - -Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. - -```javascript -myLineChart.datasets[0].points[2].value = 50; -// Would update the first dataset's value of 'March' to be 50 -myLineChart.update(); -// Calling update now animates the position of March from 90 to 50. -``` - -#### .addData( valuesArray, label ) - -Calling `addData(valuesArray, label)` on your Chart instance passing an array of values for each dataset, along with a label for those points. - -```javascript -// The values array passed into addData should be one for each dataset in the chart -myLineChart.addData([40, 60], "August"); -// This new data will now animate at the end of the chart. -``` - -#### .removeData( ) - -Calling `removeData()` on your Chart instance will remove the first value for all datasets on the chart. - -```javascript -myLineChart.removeData(); -// The chart will remove the first point and animate other points into place -``` diff --git a/web/backend/resources/bower_components/chart.js/docs/02-Bar-Chart.md b/web/backend/resources/bower_components/chart.js/docs/02-Bar-Chart.md deleted file mode 100644 index cc23f38b7..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/02-Bar-Chart.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: Bar Chart -anchor: bar-chart ---- - -### Introduction -A bar chart is a way of showing data as bars. - -It is sometimes used to show trend data, and the comparison of multiple data sets side by side. - -
    - -
    - -### Example usage -```javascript -var myBarChart = new Chart(ctx).Bar(data, options); -``` - -### Data structure - -```javascript -var data = { - labels: ["January", "February", "March", "April", "May", "June", "July"], - datasets: [ - { - label: "My First dataset", - fillColor: "rgba(220,220,220,0.5)", - strokeColor: "rgba(220,220,220,0.8)", - highlightFill: "rgba(220,220,220,0.75)", - highlightStroke: "rgba(220,220,220,1)", - data: [65, 59, 80, 81, 56, 55, 40] - }, - { - label: "My Second dataset", - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,0.8)", - highlightFill: "rgba(151,187,205,0.75)", - highlightStroke: "rgba(151,187,205,1)", - data: [28, 48, 40, 19, 86, 27, 90] - } - ] -}; -``` -The bar chart has the a very similar data structure to the line chart, and has an array of datasets, each with colours and an array of data. Again, colours are in CSS format. -We have an array of labels too for display. In the example, we are showing the same data as the previous line chart example. - -The label key on each dataset is optional, and can be used when generating a scale for the chart. - -### Chart Options - -These are the customisation options specific to Bar charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart. - -```javascript -{ - //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero : true, - - //Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - If there is a stroke on each bar - barShowStroke : true, - - //Number - Pixel width of the bar stroke - barStrokeWidth : 2, - - //Number - Spacing between each of the X value sets - barValueSpacing : 5, - - //Number - Spacing between data sets within X values - barDatasetSpacing : 1, - {% raw %} - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - {% endraw %} -} -``` - -You can override these for your `Chart` instance by passing a second argument into the `Bar` method as an object with the keys you want to override. - -For example, we could have a bar chart without a stroke on each bar by doing the following: - -```javascript -new Chart(ctx).Bar(data, { - barShowStroke: false -}); -// This will create a chart with all of the default options, merged from the global config, -// and the Bar chart defaults but this particular instance will have `barShowStroke` set to false. -``` - -We can also change these defaults values for each Bar type that is created, this object is available at `Chart.defaults.Bar`. - -### Prototype methods - -#### .getBarsAtEvent( event ) - -Calling `getBarsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the bar elements that are at that the same position of that event. - -```javascript -canvas.onclick = function(evt){ - var activeBars = myBarChart.getBarsAtEvent(evt); - // => activeBars is an array of bars on the canvas that are at the same position as the click event. -}; -``` - -This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application. - -#### .update( ) - -Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. - -```javascript -myBarChart.datasets[0].bars[2].value = 50; -// Would update the first dataset's value of 'March' to be 50 -myBarChart.update(); -// Calling update now animates the position of March from 90 to 50. -``` - -#### .addData( valuesArray, label ) - -Calling `addData(valuesArray, label)` on your Chart instance passing an array of values for each dataset, along with a label for those bars. - -```javascript -// The values array passed into addData should be one for each dataset in the chart -myBarChart.addData([40, 60], "August"); -// The new data will now animate at the end of the chart. -``` - -#### .removeData( ) - -Calling `removeData()` on your Chart instance will remove the first value for all datasets on the chart. - -```javascript -myBarChart.removeData(); -// The chart will now animate and remove the first bar -``` diff --git a/web/backend/resources/bower_components/chart.js/docs/03-Radar-Chart.md b/web/backend/resources/bower_components/chart.js/docs/03-Radar-Chart.md deleted file mode 100644 index 03dcf6e83..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/03-Radar-Chart.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Radar Chart -anchor: radar-chart ---- - -###Introduction -A radar chart is a way of showing multiple data points and the variation between them. - -They are often useful for comparing the points of two or more different data sets. - -
    - -
    - -###Example usage - -```javascript -var myRadarChart = new Chart(ctx).Radar(data, options); -``` - -###Data structure -```javascript -var data = { - labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"], - datasets: [ - { - label: "My First dataset", - fillColor: "rgba(220,220,220,0.2)", - strokeColor: "rgba(220,220,220,1)", - pointColor: "rgba(220,220,220,1)", - pointStrokeColor: "#fff", - pointHighlightFill: "#fff", - pointHighlightStroke: "rgba(220,220,220,1)", - data: [65, 59, 90, 81, 56, 55, 40] - }, - { - label: "My Second dataset", - fillColor: "rgba(151,187,205,0.2)", - strokeColor: "rgba(151,187,205,1)", - pointColor: "rgba(151,187,205,1)", - pointStrokeColor: "#fff", - pointHighlightFill: "#fff", - pointHighlightStroke: "rgba(151,187,205,1)", - data: [28, 48, 40, 19, 96, 27, 100] - } - ] -}; -``` -For a radar chart, to provide context of what each point means, we include an array of strings that show around each point in the chart. -For the radar chart data, we have an array of datasets. Each of these is an object, with a fill colour, a stroke colour, a colour for the fill of each point, and a colour for the stroke of each point. We also have an array of data values. - -The label key on each dataset is optional, and can be used when generating a scale for the chart. - -### Chart options - -These are the customisation options specific to Radar charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart. - - -```javascript -{ - //Boolean - Whether to show lines for each scale point - scaleShowLine : true, - - //Boolean - Whether we show the angle lines out of the radar - angleShowLineOut : true, - - //Boolean - Whether to show labels on the scale - scaleShowLabels : false, - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //String - Colour of the angle line - angleLineColor : "rgba(0,0,0,.1)", - - //Number - Pixel width of the angle line - angleLineWidth : 1, - - //String - Point label font declaration - pointLabelFontFamily : "'Arial'", - - //String - Point label font weight - pointLabelFontStyle : "normal", - - //Number - Point label font size in pixels - pointLabelFontSize : 10, - - //String - Point label font colour - pointLabelFontColor : "#666", - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 3, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - {% raw %} - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - {% endraw %} -} -``` - - -You can override these for your `Chart` instance by passing a second argument into the `Radar` method as an object with the keys you want to override. - -For example, we could have a radar chart without a point for each on piece of data by doing the following: - -```javascript -new Chart(ctx).Radar(data, { - pointDot: false -}); -// This will create a chart with all of the default options, merged from the global config, -// and the Bar chart defaults but this particular instance will have `pointDot` set to false. -``` - -We can also change these defaults values for each Radar type that is created, this object is available at `Chart.defaults.Radar`. - - -### Prototype methods - -#### .getPointsAtEvent( event ) - -Calling `getPointsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event. - -```javascript -canvas.onclick = function(evt){ - var activePoints = myRadarChart.getPointsAtEvent(evt); - // => activePoints is an array of points on the canvas that are at the same position as the click event. -}; -``` - -This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application. - -#### .update( ) - -Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. - -```javascript -myRadarChart.datasets[0].points[2].value = 50; -// Would update the first dataset's value of 'Sleeping' to be 50 -myRadarChart.update(); -// Calling update now animates the position of Sleeping from 90 to 50. -``` - -#### .addData( valuesArray, label ) - -Calling `addData(valuesArray, label)` on your Chart instance passing an array of values for each dataset, along with a label for those points. - -```javascript -// The values array passed into addData should be one for each dataset in the chart -myRadarChart.addData([40, 60], "Dancing"); -// The new data will now animate at the end of the chart. -``` - -#### .removeData( ) - -Calling `removeData()` on your Chart instance will remove the first value for all datasets on the chart. - -```javascript -myRadarChart.removeData(); -// Other points will now animate to their correct positions. -``` \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/docs/04-Polar-Area-Chart.md b/web/backend/resources/bower_components/chart.js/docs/04-Polar-Area-Chart.md deleted file mode 100644 index 658da54fc..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/04-Polar-Area-Chart.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Polar Area Chart -anchor: polar-area-chart ---- -### Introduction -Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment differs depending on the value. - -This type of chart is often useful when we want to show a comparison data similar to a pie chart, but also show a scale of values for context. - -
    - -
    - -### Example usage - -```javascript -new Chart(ctx).PolarArea(data, options); -``` - -### Data structure - -```javascript -var data = [ - { - value: 300, - color:"#F7464A", - highlight: "#FF5A5E", - label: "Red" - }, - { - value: 50, - color: "#46BFBD", - highlight: "#5AD3D1", - label: "Green" - }, - { - value: 100, - color: "#FDB45C", - highlight: "#FFC870", - label: "Yellow" - }, - { - value: 40, - color: "#949FB1", - highlight: "#A8B3C5", - label: "Grey" - }, - { - value: 120, - color: "#4D5360", - highlight: "#616774", - label: "Dark Grey" - } - -]; -``` -As you can see, for the chart data you pass in an array of objects, with a value and a colour. The value attribute should be a number, while the color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL. - -### Chart options - -These are the customisation options specific to Polar Area charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart. - -```javascript -{ - //Boolean - Show a backdrop to the scale label - scaleShowLabelBackdrop : true, - - //String - The colour of the label backdrop - scaleBackdropColor : "rgba(255,255,255,0.75)", - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //Number - The backdrop padding above & below the label in pixels - scaleBackdropPaddingY : 2, - - //Number - The backdrop padding to the side of the label in pixels - scaleBackdropPaddingX : 2, - - //Boolean - Show line for each value in the scale - scaleShowLine : true, - - //Boolean - Stroke a line around each segment in the chart - segmentShowStroke : true, - - //String - The colour of the stroke on each segement. - segmentStrokeColor : "#fff", - - //Number - The width of the stroke value in pixels - segmentStrokeWidth : 2, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect. - animationEasing : "easeOutBounce", - - //Boolean - Whether to animate the rotation of the chart - animateRotate : true, - - //Boolean - Whether to animate scaling the chart from the centre - animateScale : false, - {% raw %} - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    " - {% endraw %} -} -``` - -You can override these for your `Chart` instance by passing a second argument into the `PolarArea` method as an object with the keys you want to override. - -For example, we could have a polar area chart with a black stroke on each segment like so: - -```javascript -new Chart(ctx).PolarArea(data, { - segmentStrokeColor: "#000000" -}); -// This will create a chart with all of the default options, merged from the global config, -// and the PolarArea chart defaults but this particular instance will have `segmentStrokeColor` set to `"#000000"`. -``` - -We can also change these defaults values for each PolarArea type that is created, this object is available at `Chart.defaults.PolarArea`. - -### Prototype methods - -#### .getSegmentsAtEvent( event ) - -Calling `getSegmentsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the segment elements that are at that the same position of that event. - -```javascript -canvas.onclick = function(evt){ - var activePoints = myPolarAreaChart.getSegmentsAtEvent(evt); - // => activePoints is an array of segments on the canvas that are at the same position as the click event. -}; -``` - -This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application. - -#### .update( ) - -Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. - -```javascript -myPolarAreaChart.segments[1].value = 10; -// Would update the first dataset's value of 'Green' to be 10 -myPolarAreaChart.update(); -// Calling update now animates the position of Green from 50 to 10. -``` - -#### .addData( segmentData, index ) - -Calling `addData(segmentData, index)` on your Chart instance passing an object in the same format as in the constructor. There is an option second argument of 'index', this determines at what index the new segment should be inserted into the chart. - -```javascript -// An object in the same format as the original data source -myPolarAreaChart.addData({ - value: 130, - color: "#B48EAD", - highlight: "#C69CBE", - label: "Purple" -}); -// The new segment will now animate in. -``` - -#### .removeData( index ) - -Calling `removeData(index)` on your Chart instance will remove segment at that particular index. If none is provided, it will default to the last segment. - -```javascript -myPolarAreaChart.removeData(); -// Other segments will update to fill the empty space left. -``` \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/docs/05-Pie-Doughnut-Chart.md b/web/backend/resources/bower_components/chart.js/docs/05-Pie-Doughnut-Chart.md deleted file mode 100644 index dd9de6f40..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/05-Pie-Doughnut-Chart.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: Pie & Doughnut Charts -anchor: doughnut-pie-chart ---- -###Introduction -Pie and doughnut charts are probably the most commonly used chart there are. They are divided into segments, the arc of each segment shows the proportional value of each piece of data. - -They are excellent at showing the relational proportions between data. - -Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their `percentageInnerCutout`. This equates what percentage of the inner should be cut out. This defaults to `0` for pie charts, and `50` for doughnuts. - -They are also registered under two aliases in the `Chart` core. Other than their different default value, and different alias, they are exactly the same. - -
    - -
    - -
    - -
    - - -### Example usage - -```javascript -// For a pie chart -var myPieChart = new Chart(ctx[0]).Pie(data,options); - -// And for a doughnut chart -var myDoughnutChart = new Chart(ctx[1]).Doughnut(data,options); -``` - -### Data structure - -```javascript -var data = [ - { - value: 300, - color:"#F7464A", - highlight: "#FF5A5E", - label: "Red" - }, - { - value: 50, - color: "#46BFBD", - highlight: "#5AD3D1", - label: "Green" - }, - { - value: 100, - color: "#FDB45C", - highlight: "#FFC870", - label: "Yellow" - } -] -``` - -For a pie chart, you must pass in an array of objects with a value and a color property. The value attribute should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each. The color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL. - -### Chart options - -These are the customisation options specific to Pie & Doughnut charts. These options are merged with the [global chart configuration options](#getting-started-global-chart-configuration), and form the options of the chart. - -```javascript -{ - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke : true, - - //String - The colour of each segment stroke - segmentStrokeColor : "#fff", - - //Number - The width of each segment stroke - segmentStrokeWidth : 2, - - //Number - The percentage of the chart that we cut out of the middle - percentageInnerCutout : 50, // This is 0 for Pie charts - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect - animationEasing : "easeOutBounce", - - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate : true, - - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale : false, - {% raw %} - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    " - {% endraw %} -} -``` -You can override these for your `Chart` instance by passing a second argument into the `Doughnut` method as an object with the keys you want to override. - -For example, we could have a doughnut chart that animates by scaling out from the centre like so: - -```javascript -new Chart(ctx).Doughnut(data, { - animateScale: true -}); -// This will create a chart with all of the default options, merged from the global config, -// and the Doughnut chart defaults but this particular instance will have `animateScale` set to `true`. -``` - -We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.Doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.Pie`, with the only difference being `percentageInnerCutout` being set to 0. - -### Prototype methods - -#### .getSegmentsAtEvent( event ) - -Calling `getSegmentsAtEvent(event)` on your Chart instance passing an argument of an event, or jQuery event, will return the segment elements that are at the same position of that event. - -```javascript -canvas.onclick = function(evt){ - var activePoints = myDoughnutChart.getSegmentsAtEvent(evt); - // => activePoints is an array of segments on the canvas that are at the same position as the click event. -}; -``` - -This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application. - -#### .update( ) - -Calling `update()` on your Chart instance will re-render the chart with any updated values, allowing you to edit the value of multiple existing points, then render those in one animated render loop. - -```javascript -myDoughnutChart.segments[1].value = 10; -// Would update the first dataset's value of 'Green' to be 10 -myDoughnutChart.update(); -// Calling update now animates the circumference of the segment 'Green' from 50 to 10. -// and transitions other segment widths -``` - -#### .addData( segmentData, index ) - -Calling `addData(segmentData, index)` on your Chart instance passing an object in the same format as in the constructor. There is an optional second argument of 'index', this determines at what index the new segment should be inserted into the chart. - -```javascript -// An object in the same format as the original data source -myDoughnutChart.addData({ - value: 130, - color: "#B48EAD", - highlight: "#C69CBE", - label: "Purple" -}); -// The new segment will now animate in. -``` - -#### .removeData( index ) - -Calling `removeData(index)` on your Chart instance will remove segment at that particular index. If none is provided, it will default to the last segment. - -```javascript -myDoughnutChart.removeData(); -// Other segments will update to fill the empty space left. -``` diff --git a/web/backend/resources/bower_components/chart.js/docs/06-Advanced.md b/web/backend/resources/bower_components/chart.js/docs/06-Advanced.md deleted file mode 100644 index f0495bfbb..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/06-Advanced.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Advanced usage -anchor: advanced-usage ---- - - -### Prototype methods - -For each chart, there are a set of global prototype methods on the shared `ChartType` which you may find useful. These are available on all charts created with Chart.js, but for the examples, let's use a line chart we've made. - -```javascript -// For example: -var myLineChart = new Chart(ctx).Line(data); -``` - -#### .clear() - -Will clear the chart canvas. Used extensively internally between animation frames, but you might find it useful. - -```javascript -// Will clear the canvas that myLineChart is drawn on -myLineChart.clear(); -// => returns 'this' for chainability -``` - -#### .stop() - -Use this to stop any current animation loop. This will pause the chart during any current animation frame. Call `.render()` to re-animate. - -```javascript -// Stops the charts animation loop at its current frame -myLineChart.stop(); -// => returns 'this' for chainability -``` - -#### .resize() - -Use this to manually resize the canvas element. This is run each time the browser is resized, but you can call this method manually if you change the size of the canvas nodes container element. - -```javascript -// Resizes & redraws to fill its container element -myLineChart.resize(); -// => returns 'this' for chainability -``` - -#### .destroy() - -Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js. - -```javascript -// Destroys a specific chart instance -myLineChart.destroy(); -``` - -#### .toBase64Image() - -This returns a base 64 encoded string of the chart in it's current state. - -```javascript -myLineChart.toBase64Image(); -// => returns png data url of the image on the canvas -``` - -#### .generateLegend() - -Returns an HTML string of a legend for that chart. The template for this legend is at `legendTemplate` in the chart options. - -```javascript -myLineChart.generateLegend(); -// => returns HTML string of a legend for this chart -``` - -### External Tooltips - -You can enable custom tooltips in the global or chart configuration like so: - -```javascript -var myPieChart = new Chart(ctx).Pie(data, { - customTooltips: function(tooltip) { - - // tooltip will be false if tooltip is not visible or should be hidden - if (!tooltip) { - return; - } - - // Otherwise, tooltip will be an object with all tooltip properties like: - - // tooltip.caretHeight - // tooltip.caretPadding - // tooltip.chart - // tooltip.cornerRadius - // tooltip.fillColor - // tooltip.font... - // tooltip.text - // tooltip.x - // tooltip.y - // etc... - - }; -}); -``` - -See files `sample/pie-customTooltips.html` and `sample/line-customTooltips.html` for examples on how to get started. - - -### Writing new chart types - -Chart.js 1.0 has been rewritten to provide a platform for developers to create their own custom chart types, and be able to share and utilise them through the Chart.js API. - -The format is relatively simple, there are a set of utility helper methods under `Chart.helpers`, including things such as looping over collections, requesting animation frames, and easing equations. - -On top of this, there are also some simple base classes of Chart elements, these all extend from `Chart.Element`, and include things such as points, bars and scales. - -```javascript -Chart.Type.extend({ - // Passing in a name registers this chart in the Chart namespace - name: "Scatter", - // Providing a defaults will also register the deafults in the chart namespace - defaults : { - options: "Here", - available: "at this.options" - }, - // Initialize is fired when the chart is initialized - Data is passed in as a parameter - // Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - this.chart.ctx // The drawing context for this chart - this.chart.canvas // the canvas node for this chart - }, - // Used to draw something on the canvas - draw: function() { - } -}); - -// Now we can create a new instance of our chart, using the Chart.js API -new Chart(ctx).Scatter(data); -// initialize is now run -``` - -### Extending existing chart types - -We can also extend existing chart types, and expose them to the API in the same way. Let's say for example, we might want to run some more code when we initialize every Line chart. - -```javascript -// Notice now we're extending the particular Line chart type, rather than the base class. -Chart.types.Line.extend({ - // Passing in a name registers this chart in the Chart namespace in the same way - name: "LineAlt", - initialize: function(data){ - console.log('My Line chart extension'); - Chart.types.Line.prototype.initialize.apply(this, arguments); - } -}); - -// Creates a line chart in the same way -new Chart(ctx).LineAlt(data); -// but this logs 'My Line chart extension' in the console. -``` - -### Community extensions - -- Stacked Bar Chart by @Regaddi -- Error bars (bar and line charts) by @CAYdenberg - -### Creating custom builds - -Chart.js uses gulp to build the library into a single JavaScript file. We can use this same build script with custom parameters in order to build a custom version. - -Firstly, we need to ensure development dependencies are installed. With node and npm installed, after cloning the Chart.js repo to a local directory, and navigating to that directory in the command line, we can run the following: - -```bash -npm install -npm install -g gulp -``` - -This will install the local development dependencies for Chart.js, along with a CLI for the JavaScript task runner gulp. - -Now, we can run the `gulp build` task, and pass in a comma seperated list of types as an argument to build a custom version of Chart.js with only specified chart types. - -Here we will create a version of Chart.js with only Line, Radar and Bar charts included: - -```bash -gulp build --types=Line,Radar,Bar -``` - -This will output to the `/custom` directory, and write two files, Chart.js, and Chart.min.js with only those chart types included. diff --git a/web/backend/resources/bower_components/chart.js/docs/07-Notes.md b/web/backend/resources/bower_components/chart.js/docs/07-Notes.md deleted file mode 100644 index 8ba5a59dd..000000000 --- a/web/backend/resources/bower_components/chart.js/docs/07-Notes.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Notes -anchor: notes ---- - -### Browser support -Browser support for the canvas element is available in all modern & major mobile browsers (caniuse.com/canvas). - -For IE8 & below, I would recommend using the polyfill ExplorerCanvas - available at https://code.google.com/p/explorercanvas/. It falls back to Internet explorer's format VML when canvas support is not available. Example use: - -```html - - - -``` - -Usually I would recommend feature detection to choose whether or not to load a polyfill, rather than IE conditional comments, however in this case, VML is a Microsoft proprietary format, so it will only work in IE. - -Some important points to note in my experience using ExplorerCanvas as a fallback. - -- Initialise charts on load rather than DOMContentReady when using the library, as sometimes a race condition will occur, and it will result in an error when trying to get the 2d context of a canvas. -- New VML DOM elements are being created for each animation frame and there is no hardware acceleration. As a result animation is usually slow and jerky, with flashing text. It is a good idea to dynamically turn off animation based on canvas support. I recommend using the excellent Modernizr to do this. -- When declaring fonts, the library explorercanvas requires the font name to be in single quotes inside the string. For example, instead of your scaleFontFamily property being simply "Arial", explorercanvas support, use "'Arial'" instead. Chart.js does this for default values. - -### Bugs & issues - -Please report these on the GitHub page - at github.com/nnnick/Chart.js. If you could include a link to a simple jsbin or similar to demonstrate the issue, that'd be really helpful. - - -### Contributing -New contributions to the library are welcome, just a couple of guidelines: - -- Tabs for indentation, not spaces please. -- Please ensure you're changing the individual files in `/src`, not the concatenated output in the `Chart.js` file in the root of the repo. -- Please check that your code will pass `jshint` code standards, `gulp jshint` will run this for you. -- Please keep pull requests concise, and document new functionality in the relevant `.md` file. -- Consider whether your changes are useful for all users, or if creating a Chart.js extension would be more appropriate. - -### License -Chart.js is open source and available under the MIT license. \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/gulpfile.js b/web/backend/resources/bower_components/chart.js/gulpfile.js deleted file mode 100644 index e8fe1c711..000000000 --- a/web/backend/resources/bower_components/chart.js/gulpfile.js +++ /dev/null @@ -1,137 +0,0 @@ -var gulp = require('gulp'), - concat = require('gulp-concat'), - uglify = require('gulp-uglify'), - util = require('gulp-util'), - jshint = require('gulp-jshint'), - size = require('gulp-size'), - connect = require('gulp-connect'), - replace = require('gulp-replace'), - htmlv = require('gulp-html-validator'), - inquirer = require('inquirer'), - semver = require('semver'), - exec = require('child_process').exec, - fs = require('fs'), - package = require('./package.json'), - bower = require('./bower.json'); - -var srcDir = './src/'; -/* - * Usage : gulp build --types=Bar,Line,Doughnut - * Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together - * - A minified version of this code, in Chart.min.js - */ - -gulp.task('build', function(){ - - // Default to all of the chart types, with Chart.Core first - var srcFiles = [FileName('Core')], - isCustom = !!(util.env.types), - outputDir = (isCustom) ? 'custom' : '.'; - if (isCustom){ - util.env.types.split(',').forEach(function(type){ return srcFiles.push(FileName(type))}); - } - else{ - // Seems gulp-concat remove duplicates - nice! - // So we can use this to sort out dependency order - aka include Core first! - srcFiles.push(srcDir+'*'); - } - - return gulp.src(srcFiles) - .pipe(concat('Chart.js')) - .pipe(replace('{{ version }}', package.version)) - .pipe(gulp.dest(outputDir)) - .pipe(uglify({preserveComments:'some'})) - .pipe(concat('Chart.min.js')) - .pipe(gulp.dest(outputDir)); - - function FileName(moduleName){ - return srcDir+'Chart.'+moduleName+'.js'; - }; -}); - -/* - * Usage : gulp bump - * Prompts: Version increment to bump - * Output: - New version number written into package.json & bower.json - */ - -gulp.task('bump', function(complete){ - util.log('Current version:', util.colors.cyan(package.version)); - var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType){ - return versionType + ' (v' + semver.inc(package.version, versionType) + ')'; - }); - inquirer.prompt({ - type: 'list', - name: 'version', - message: 'What version update would you like?', - choices: choices - }, function(res){ - var increment = res.version.split(' ')[0], - newVersion = semver.inc(package.version, increment); - - // Set the new versions into the bower/package object - package.version = newVersion; - bower.version = newVersion; - - // Write these to their own files, then build the output - fs.writeFileSync('package.json', JSON.stringify(package, null, 2)); - fs.writeFileSync('bower.json', JSON.stringify(bower, null, 2)); - - complete(); - }); -}); - -gulp.task('release', ['build'], function(){ - exec('git tag -a v' + package.version); -}); - -gulp.task('jshint', function(){ - return gulp.src(srcDir + '*.js') - .pipe(jshint()) - .pipe(jshint.reporter('default')); -}); - -gulp.task('valid', function(){ - return gulp.src('samples/*.html') - .pipe(htmlv()); -}); - -gulp.task('library-size', function(){ - return gulp.src('Chart.min.js') - .pipe(size({ - gzip: true - })); -}); - -gulp.task('module-sizes', function(){ - return gulp.src(srcDir + '*.js') - .pipe(uglify({preserveComments:'some'})) - .pipe(size({ - showFiles: true, - gzip: true - })) -}); - -gulp.task('watch', function(){ - gulp.watch('./src/*', ['build']); -}); - -gulp.task('test', ['jshint', 'valid']); - -gulp.task('size', ['library-size', 'module-sizes']); - -gulp.task('default', ['build', 'watch']); - -gulp.task('server', function(){ - connect.server({ - port: 8000 - }); -}); - -// Convenience task for opening the project straight from the command line -gulp.task('_open', function(){ - exec('open http://localhost:8000'); - exec('subl .'); -}); - -gulp.task('dev', ['server', 'default']); diff --git a/web/backend/resources/bower_components/chart.js/package.json b/web/backend/resources/bower_components/chart.js/package.json deleted file mode 100644 index 9ac4a0e71..000000000 --- a/web/backend/resources/bower_components/chart.js/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "chart.js", - "homepage": "http://www.chartjs.org", - "description": "Simple HTML5 charts using the canvas element.", - "version": "1.0.2", - "main": "Chart.js", - "repository": { - "type": "git", - "url": "https://github.com/nnnick/Chart.js.git" - }, - "dependences": {}, - "devDependencies": { - "gulp": "3.5.x", - "gulp-concat": "~2.1.x", - "gulp-connect": "~2.0.5", - "gulp-jshint": "~1.5.1", - "gulp-replace": "^0.4.0", - "gulp-size": "~0.4.0", - "gulp-uglify": "~0.2.x", - "gulp-util": "~2.2.x", - "gulp-html-validator": "^0.0.2", - "inquirer": "^0.5.1", - "semver": "^3.0.1" - }, - "spm": { - "main": "Chart.js" - } -} \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/samples/bar.html b/web/backend/resources/bower_components/chart.js/samples/bar.html deleted file mode 100644 index 5bf4b5bae..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/bar.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - Bar Chart - - - -
    - -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/doughnut.html b/web/backend/resources/bower_components/chart.js/samples/doughnut.html deleted file mode 100644 index fdf7539a8..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/doughnut.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - Doughnut Chart - - - - -
    - -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/line-customTooltips.html b/web/backend/resources/bower_components/chart.js/samples/line-customTooltips.html deleted file mode 100644 index 4dc46e1ab..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/line-customTooltips.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - Line Chart with Custom Tooltips - - - - - - - -
    - -
    -
    - -
    - -
    - - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/line.html b/web/backend/resources/bower_components/chart.js/samples/line.html deleted file mode 100644 index ccd0dad96..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/line.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - Line Chart - - - -
    -
    - -
    -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/pie-customTooltips.html b/web/backend/resources/bower_components/chart.js/samples/pie-customTooltips.html deleted file mode 100644 index 732317de4..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/pie-customTooltips.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Pie Chart with Custom Tooltips - - - - - - - -
    - -
    -
    - -
    - -
    - - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/pie.html b/web/backend/resources/bower_components/chart.js/samples/pie.html deleted file mode 100644 index 255a49976..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/pie.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - Pie Chart - - - -
    - -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/polar-area.html b/web/backend/resources/bower_components/chart.js/samples/polar-area.html deleted file mode 100644 index d3d3f01b4..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/polar-area.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - Polar Area Chart - - - -
    - -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/samples/radar.html b/web/backend/resources/bower_components/chart.js/samples/radar.html deleted file mode 100644 index 6a04f879c..000000000 --- a/web/backend/resources/bower_components/chart.js/samples/radar.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - Radar Chart - - - - - -
    - -
    - - - - - diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.Bar.js b/web/backend/resources/bower_components/chart.js/src/Chart.Bar.js deleted file mode 100644 index 87fb26ddc..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.Bar.js +++ /dev/null @@ -1,302 +0,0 @@ -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - var defaultConfig = { - //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero : true, - - //Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - If there is a stroke on each bar - barShowStroke : true, - - //Number - Pixel width of the bar stroke - barStrokeWidth : 2, - - //Number - Spacing between each of the X value sets - barValueSpacing : 5, - - //Number - Spacing between data sets within X values - barDatasetSpacing : 1, - - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - - }; - - - Chart.Type.extend({ - name: "Bar", - defaults : defaultConfig, - initialize: function(data){ - - //Expose options as a scope variable here so we can access it in the ScaleClass - var options = this.options; - - this.ScaleClass = Chart.Scale.extend({ - offsetGridLines : true, - calculateBarX : function(datasetCount, datasetIndex, barIndex){ - //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar - var xWidth = this.calculateBaseWidth(), - xAbsolute = this.calculateX(barIndex) - (xWidth/2), - barWidth = this.calculateBarWidth(datasetCount); - - return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2; - }, - calculateBaseWidth : function(){ - return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing); - }, - calculateBarWidth : function(datasetCount){ - //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset - var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing); - - return (baseWidth / datasetCount); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : []; - - this.eachBars(function(bar){ - bar.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activeBars, function(activeBar){ - activeBar.fillColor = activeBar.highlightFill; - activeBar.strokeColor = activeBar.highlightStroke; - }); - this.showTooltip(activeBars); - }); - } - - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.BarClass = Chart.Rectangle.extend({ - strokeWidth : this.options.barStrokeWidth, - showStroke : this.options.barShowStroke, - ctx : this.chart.ctx - }); - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset,datasetIndex){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - bars : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.bars.push(new this.BarClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.strokeColor, - fillColor : dataset.fillColor, - highlightFill : dataset.highlightFill || dataset.fillColor, - highlightStroke : dataset.highlightStroke || dataset.strokeColor - })); - },this); - - },this); - - this.buildScale(data.labels); - - this.BarClass.prototype.base = this.scale.endPoint; - - this.eachBars(function(bar, index, datasetIndex){ - helpers.extend(bar, { - width : this.scale.calculateBarWidth(this.datasets.length), - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y: this.scale.endPoint - }); - bar.save(); - }, this); - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - - this.eachBars(function(bar){ - bar.save(); - }); - this.render(); - }, - eachBars : function(callback){ - helpers.each(this.datasets,function(dataset, datasetIndex){ - helpers.each(dataset.bars, callback, this, datasetIndex); - },this); - }, - getBarsAtEvent : function(e){ - var barsArray = [], - eventPosition = helpers.getRelativePosition(e), - datasetIterator = function(dataset){ - barsArray.push(dataset.bars[barIndex]); - }, - barIndex; - - for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) { - for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) { - if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){ - helpers.each(this.datasets, datasetIterator); - return barsArray; - } - } - } - - return barsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachBars(function(bar){ - values.push(bar.value); - }); - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange: function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - this.scale = new this.ScaleClass(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].bars.push(new this.BarClass({ - value : value, - label : label, - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1), - y: this.scale.endPoint, - width : this.scale.calculateBarWidth(this.datasets.length), - base : this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].strokeColor, - fillColor : this.datasets[datasetIndex].fillColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.bars.shift(); - },this); - this.update(); - }, - reflow : function(){ - helpers.extend(this.BarClass.prototype,{ - y: this.scale.endPoint, - base : this.scale.endPoint - }); - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - this.scale.draw(easingDecimal); - - //Draw all the bars for each dataset - helpers.each(this.datasets,function(dataset,datasetIndex){ - helpers.each(dataset.bars,function(bar,index){ - if (bar.hasValue()){ - bar.base = this.scale.endPoint; - //Transition then draw - bar.transition({ - x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y : this.scale.calculateY(bar.value), - width : this.scale.calculateBarWidth(this.datasets.length) - }, easingDecimal).draw(); - } - },this); - - },this); - } - }); - - -}).call(this); diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.Core.js b/web/backend/resources/bower_components/chart.js/src/Chart.Core.js deleted file mode 100644 index 5dccd2ef9..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.Core.js +++ /dev/null @@ -1,2021 +0,0 @@ -/*! - * Chart.js - * http://chartjs.org/ - * Version: {{ version }} - * - * Copyright 2015 Nick Downie - * Released under the MIT license - * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md - */ - - -(function(){ - - "use strict"; - - //Declare root variable - window in the browser, global on the server - var root = this, - previous = root.Chart; - - //Occupy the global variable of Chart, and create a simple base class - var Chart = function(context){ - var chart = this; - this.canvas = context.canvas; - - this.ctx = context; - - //Variables global to the chart - var computeDimension = function(element,dimension) - { - if (element['offset'+dimension]) - { - return element['offset'+dimension]; - } - else - { - return document.defaultView.getComputedStyle(element).getPropertyValue(dimension); - } - } - - var width = this.width = computeDimension(context.canvas,'Width'); - var height = this.height = computeDimension(context.canvas,'Height'); - - // Firefox requires this to work correctly - context.canvas.width = width; - context.canvas.height = height; - - var width = this.width = context.canvas.width; - var height = this.height = context.canvas.height; - this.aspectRatio = this.width / this.height; - //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. - helpers.retinaScale(this); - - return this; - }; - //Globally expose the defaults to allow for user updating/changing - Chart.defaults = { - global: { - // Boolean - Whether to animate the chart - animation: true, - - // Number - Number of animation steps - animationSteps: 60, - - // String - Animation easing effect - animationEasing: "easeOutQuart", - - // Boolean - If we should show the scale at all - showScale: true, - - // Boolean - If we want to override with a hard coded scale - scaleOverride: false, - - // ** Required if scaleOverride is true ** - // Number - The number of steps in a hard coded scale - scaleSteps: null, - // Number - The value jump in the hard coded scale - scaleStepWidth: null, - // Number - The scale starting value - scaleStartValue: null, - - // String - Colour of the scale line - scaleLineColor: "rgba(0,0,0,.1)", - - // Number - Pixel width of the scale line - scaleLineWidth: 1, - - // Boolean - Whether to show labels on the scale - scaleShowLabels: true, - - // Interpolated JS string - can access value - scaleLabel: "<%=value%>", - - // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there - scaleIntegersOnly: true, - - // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero: false, - - // String - Scale label font declaration for the scale label - scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Scale label font size in pixels - scaleFontSize: 12, - - // String - Scale label font weight style - scaleFontStyle: "normal", - - // String - Scale label font colour - scaleFontColor: "#666", - - // Boolean - whether or not the chart should be responsive and resize when the browser does. - responsive: false, - - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: true, - - // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove - showTooltips: true, - - // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function - customTooltips: false, - - // Array - Array of string names to attach tooltip events - tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], - - // String - Tooltip background colour - tooltipFillColor: "rgba(0,0,0,0.8)", - - // String - Tooltip label font declaration for the scale label - tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip label font size in pixels - tooltipFontSize: 14, - - // String - Tooltip font weight style - tooltipFontStyle: "normal", - - // String - Tooltip label font colour - tooltipFontColor: "#fff", - - // String - Tooltip title font declaration for the scale label - tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip title font size in pixels - tooltipTitleFontSize: 14, - - // String - Tooltip title font weight style - tooltipTitleFontStyle: "bold", - - // String - Tooltip title font colour - tooltipTitleFontColor: "#fff", - - // Number - pixel width of padding around tooltip text - tooltipYPadding: 6, - - // Number - pixel width of padding around tooltip text - tooltipXPadding: 6, - - // Number - Size of the caret on the tooltip - tooltipCaretSize: 8, - - // Number - Pixel radius of the tooltip border - tooltipCornerRadius: 6, - - // Number - Pixel offset from point x to tooltip edge - tooltipXOffset: 10, - - // String - Template string for single tooltips - tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>", - - // String - Template string for single tooltips - multiTooltipTemplate: "<%= value %>", - - // String - Colour behind the legend colour block - multiTooltipKeyBackground: '#fff', - - // Function - Will fire on animation progression. - onAnimationProgress: function(){}, - - // Function - Will fire on animation completion. - onAnimationComplete: function(){} - - } - }; - - //Create a dictionary of chart types, to allow for extension of existing types - Chart.types = {}; - - //Global Chart helpers object for utility methods and classes - var helpers = Chart.helpers = {}; - - //-- Basic js utility methods - var each = helpers.each = function(loopable,callback,self){ - var additionalArgs = Array.prototype.slice.call(arguments, 3); - // Check to see if null or undefined firstly. - if (loopable){ - if (loopable.length === +loopable.length){ - var i; - for (i=0; i= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)){ - return currentItem; - } - } - }, - inherits = helpers.inherits = function(extensions){ - //Basic javascript inheritance based on the model created in Backbone.js - var parent = this; - var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); }; - - var Surrogate = function(){ this.constructor = ChartElement;}; - Surrogate.prototype = parent.prototype; - ChartElement.prototype = new Surrogate(); - - ChartElement.extend = inherits; - - if (extensions) extend(ChartElement.prototype, extensions); - - ChartElement.__super__ = parent.prototype; - - return ChartElement; - }, - noop = helpers.noop = function(){}, - uid = helpers.uid = (function(){ - var id=0; - return function(){ - return "chart-" + id++; - }; - })(), - warn = helpers.warn = function(str){ - //Method for warning of errors - if (window.console && typeof window.console.warn == "function") console.warn(str); - }, - amd = helpers.amd = (typeof define == 'function' && define.amd), - //-- Math methods - isNumber = helpers.isNumber = function(n){ - return !isNaN(parseFloat(n)) && isFinite(n); - }, - max = helpers.max = function(array){ - return Math.max.apply( Math, array ); - }, - min = helpers.min = function(array){ - return Math.min.apply( Math, array ); - }, - cap = helpers.cap = function(valueToCap,maxValue,minValue){ - if(isNumber(maxValue)) { - if( valueToCap > maxValue ) { - return maxValue; - } - } - else if(isNumber(minValue)){ - if ( valueToCap < minValue ){ - return minValue; - } - } - return valueToCap; - }, - getDecimalPlaces = helpers.getDecimalPlaces = function(num){ - if (num%1!==0 && isNumber(num)){ - return num.toString().split(".")[1].length; - } - else { - return 0; - } - }, - toRadians = helpers.radians = function(degrees){ - return degrees * (Math.PI/180); - }, - // Gets the angle from vertical upright to the point about a centre. - getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){ - var distanceFromXCenter = anglePoint.x - centrePoint.x, - distanceFromYCenter = anglePoint.y - centrePoint.y, - radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - - var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter); - - //If the segment is in the top left quadrant, we need to add another rotation to the angle - if (distanceFromXCenter < 0 && distanceFromYCenter < 0){ - angle += Math.PI*2; - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }, - aliasPixel = helpers.aliasPixel = function(pixelWidth){ - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }, - splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){ - //Props to Rob Spencer at scaled innovation for his post on splining between points - //http://scaledinnovation.com/analytics/splines/aboutSplines.html - var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)), - d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)), - fa=t*d01/(d01+d12),// scaling factor for triangle Ta - fb=t*d12/(d01+d12); - return { - inner : { - x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y) - }, - outer : { - x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y) - } - }; - }, - calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){ - return Math.floor(Math.log(val) / Math.LN10); - }, - calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){ - - //Set a minimum step of two - a point at the top of the graph, and a point at the base - var minSteps = 2, - maxSteps = Math.floor(drawingSize/(textSize * 1.5)), - skipFitting = (minSteps >= maxSteps); - - var maxValue = max(valuesArray), - minValue = min(valuesArray); - - // We need some degree of seperation here to calculate the scales if all the values are the same - // Adding/minusing 0.5 will give us a range of 1. - if (maxValue === minValue){ - maxValue += 0.5; - // So we don't end up with a graph with a negative start value if we've said always start from zero - if (minValue >= 0.5 && !startFromZero){ - minValue -= 0.5; - } - else{ - // Make up a whole number above the values - maxValue += 0.5; - } - } - - var valueRange = Math.abs(maxValue - minValue), - rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange), - graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphRange = graphMax - graphMin, - stepValue = Math.pow(10, rangeOrderOfMagnitude), - numberOfSteps = Math.round(graphRange / stepValue); - - //If we have more space on the graph we'll use it to give more definition to the data - while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) { - if(numberOfSteps > maxSteps){ - stepValue *=2; - numberOfSteps = Math.round(graphRange/stepValue); - // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps. - if (numberOfSteps % 1 !== 0){ - skipFitting = true; - } - } - //We can fit in double the amount of scale points on the scale - else{ - //If user has declared ints only, and the step value isn't a decimal - if (integersOnly && rangeOrderOfMagnitude >= 0){ - //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float - if(stepValue/2 % 1 === 0){ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - //If it would make it a float break out of the loop - else{ - break; - } - } - //If the scale doesn't have to be an int, make the scale more granular anyway. - else{ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - - } - } - - if (skipFitting){ - numberOfSteps = minSteps; - stepValue = graphRange / numberOfSteps; - } - - return { - steps : numberOfSteps, - stepValue : stepValue, - min : graphMin, - max : graphMin + (numberOfSteps * stepValue) - }; - - }, - /* jshint ignore:start */ - // Blows up jshint errors based on the new Function constructor - //Templating methods - //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ - template = helpers.template = function(templateString, valuesObject){ - - // If templateString is function rather than string-template - call the function for valuesObject - - if(templateString instanceof Function){ - return templateString(valuesObject); - } - - var cache = {}; - function tmpl(str, data){ - // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. - var fn = !/\W/.test(str) ? - cache[str] = cache[str] : - - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - new Function("obj", - "var p=[],print=function(){p.push.apply(p,arguments);};" + - - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + - - // Convert the template into pure JavaScript - str - .replace(/[\r\t\n]/g, " ") - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)'/g, "$1\r") - .replace(/\t=(.*?)%>/g, "',$1,'") - .split("\t").join("');") - .split("%>").join("p.push('") - .split("\r").join("\\'") + - "');}return p.join('');" - ); - - // Provide some basic currying to the user - return data ? fn( data ) : fn; - } - return tmpl(templateString,valuesObject); - }, - /* jshint ignore:end */ - generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){ - var labelsArray = new Array(numberOfSteps); - if (labelTemplateString){ - each(labelsArray,function(val,index){ - labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))}); - }); - } - return labelsArray; - }, - //--Animation methods - //Easing functions adapted from Robert Penner's easing equations - //http://www.robertpenner.com/easing/ - easingEffects = helpers.easingEffects = { - linear: function (t) { - return t; - }, - easeInQuad: function (t) { - return t * t; - }, - easeOutQuad: function (t) { - return -1 * t * (t - 2); - }, - easeInOutQuad: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t; - return -1 / 2 * ((--t) * (t - 2) - 1); - }, - easeInCubic: function (t) { - return t * t * t; - }, - easeOutCubic: function (t) { - return 1 * ((t = t / 1 - 1) * t * t + 1); - }, - easeInOutCubic: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t; - return 1 / 2 * ((t -= 2) * t * t + 2); - }, - easeInQuart: function (t) { - return t * t * t * t; - }, - easeOutQuart: function (t) { - return -1 * ((t = t / 1 - 1) * t * t * t - 1); - }, - easeInOutQuart: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t; - return -1 / 2 * ((t -= 2) * t * t * t - 2); - }, - easeInQuint: function (t) { - return 1 * (t /= 1) * t * t * t * t; - }, - easeOutQuint: function (t) { - return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); - }, - easeInOutQuint: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t; - return 1 / 2 * ((t -= 2) * t * t * t * t + 2); - }, - easeInSine: function (t) { - return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; - }, - easeOutSine: function (t) { - return 1 * Math.sin(t / 1 * (Math.PI / 2)); - }, - easeInOutSine: function (t) { - return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); - }, - easeInExpo: function (t) { - return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); - }, - easeOutExpo: function (t) { - return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); - }, - easeInOutExpo: function (t) { - if (t === 0) return 0; - if (t === 1) return 1; - if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1)); - return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); - }, - easeInCirc: function (t) { - if (t >= 1) return t; - return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); - }, - easeOutCirc: function (t) { - return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); - }, - easeInOutCirc: function (t) { - if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1); - return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - easeInElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - }, - easeOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; - }, - easeInOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1 / 2) == 2) return 1; - if (!p) p = 1 * (0.3 * 1.5); - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function (t) { - var s = 1.70158; - return 1 * (t /= 1) * t * ((s + 1) * t - s); - }, - easeOutBack: function (t) { - var s = 1.70158; - return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); - }, - easeInOutBack: function (t) { - var s = 1.70158; - if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); - return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - easeInBounce: function (t) { - return 1 - easingEffects.easeOutBounce(1 - t); - }, - easeOutBounce: function (t) { - if ((t /= 1) < (1 / 2.75)) { - return 1 * (7.5625 * t * t); - } else if (t < (2 / 2.75)) { - return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); - } else if (t < (2.5 / 2.75)) { - return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); - } else { - return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); - } - }, - easeInOutBounce: function (t) { - if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5; - return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; - } - }, - //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - requestAnimFrame = helpers.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - })(), - cancelAnimFrame = helpers.cancelAnimFrame = (function(){ - return window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(callback) { - return window.clearTimeout(callback, 1000 / 60); - }; - })(), - animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){ - - var currentStep = 0, - easingFunction = easingEffects[easingString] || easingEffects.linear; - - var animationFrame = function(){ - currentStep++; - var stepDecimal = currentStep/totalSteps; - var easeDecimal = easingFunction(stepDecimal); - - callback.call(chartInstance,easeDecimal,stepDecimal, currentStep); - onProgress.call(chartInstance,easeDecimal,stepDecimal); - if (currentStep < totalSteps){ - chartInstance.animationFrame = requestAnimFrame(animationFrame); - } else{ - onComplete.apply(chartInstance); - } - }; - requestAnimFrame(animationFrame); - }, - //-- DOM methods - getRelativePosition = helpers.getRelativePosition = function(evt){ - var mouseX, mouseY; - var e = evt.originalEvent || evt, - canvas = evt.currentTarget || evt.srcElement, - boundingRect = canvas.getBoundingClientRect(); - - if (e.touches){ - mouseX = e.touches[0].clientX - boundingRect.left; - mouseY = e.touches[0].clientY - boundingRect.top; - - } - else{ - mouseX = e.clientX - boundingRect.left; - mouseY = e.clientY - boundingRect.top; - } - - return { - x : mouseX, - y : mouseY - }; - - }, - addEvent = helpers.addEvent = function(node,eventType,method){ - if (node.addEventListener){ - node.addEventListener(eventType,method); - } else if (node.attachEvent){ - node.attachEvent("on"+eventType, method); - } else { - node["on"+eventType] = method; - } - }, - removeEvent = helpers.removeEvent = function(node, eventType, handler){ - if (node.removeEventListener){ - node.removeEventListener(eventType, handler, false); - } else if (node.detachEvent){ - node.detachEvent("on"+eventType,handler); - } else{ - node["on" + eventType] = noop; - } - }, - bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){ - // Create the events object if it's not already present - if (!chartInstance.events) chartInstance.events = {}; - - each(arrayOfEvents,function(eventName){ - chartInstance.events[eventName] = function(){ - handler.apply(chartInstance, arguments); - }; - addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]); - }); - }, - unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) { - each(arrayOfEvents, function(handler,eventName){ - removeEvent(chartInstance.chart.canvas, eventName, handler); - }); - }, - getMaximumWidth = helpers.getMaximumWidth = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientWidth; - }, - getMaximumHeight = helpers.getMaximumHeight = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientHeight; - }, - getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support - retinaScale = helpers.retinaScale = function(chart){ - var ctx = chart.ctx, - width = chart.canvas.width, - height = chart.canvas.height; - - if (window.devicePixelRatio) { - ctx.canvas.style.width = width + "px"; - ctx.canvas.style.height = height + "px"; - ctx.canvas.height = height * window.devicePixelRatio; - ctx.canvas.width = width * window.devicePixelRatio; - ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - } - }, - //-- Canvas methods - clear = helpers.clear = function(chart){ - chart.ctx.clearRect(0,0,chart.width,chart.height); - }, - fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){ - return fontStyle + " " + pixelSize+"px " + fontFamily; - }, - longestText = helpers.longestText = function(ctx,font,arrayOfStrings){ - ctx.font = font; - var longest = 0; - each(arrayOfStrings,function(string){ - var textWidth = ctx.measureText(string).width; - longest = (textWidth > longest) ? textWidth : longest; - }); - return longest; - }, - drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){ - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - }; - - - //Store a reference to each instance - allowing us to globally resize chart instances on window resize. - //Destroy method on the chart will remove the instance of the chart from this reference. - Chart.instances = {}; - - Chart.Type = function(data,options,chart){ - this.options = options; - this.chart = chart; - this.id = uid(); - //Add the chart instance to the global namespace - Chart.instances[this.id] = this; - - // Initialize is always called when a chart type is created - // By default it is a no op, but it should be extended - if (options.responsive){ - this.resize(); - } - this.initialize.call(this,data); - }; - - //Core methods that'll be a part of every chart type - extend(Chart.Type.prototype,{ - initialize : function(){return this;}, - clear : function(){ - clear(this.chart); - return this; - }, - stop : function(){ - // Stops any current animation loop occuring - cancelAnimFrame(this.animationFrame); - return this; - }, - resize : function(callback){ - this.stop(); - var canvas = this.chart.canvas, - newWidth = getMaximumWidth(this.chart.canvas), - newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas); - - canvas.width = this.chart.width = newWidth; - canvas.height = this.chart.height = newHeight; - - retinaScale(this.chart); - - if (typeof callback === "function"){ - callback.apply(this, Array.prototype.slice.call(arguments, 1)); - } - return this; - }, - reflow : noop, - render : function(reflow){ - if (reflow){ - this.reflow(); - } - if (this.options.animation && !reflow){ - helpers.animationLoop( - this.draw, - this.options.animationSteps, - this.options.animationEasing, - this.options.onAnimationProgress, - this.options.onAnimationComplete, - this - ); - } - else{ - this.draw(); - this.options.onAnimationComplete.call(this); - } - return this; - }, - generateLegend : function(){ - return template(this.options.legendTemplate,this); - }, - destroy : function(){ - this.clear(); - unbindEvents(this, this.events); - var canvas = this.chart.canvas; - - // Reset canvas height/width attributes starts a fresh with the canvas context - canvas.width = this.chart.width; - canvas.height = this.chart.height; - - // < IE9 doesn't support removeProperty - if (canvas.style.removeProperty) { - canvas.style.removeProperty('width'); - canvas.style.removeProperty('height'); - } else { - canvas.style.removeAttribute('width'); - canvas.style.removeAttribute('height'); - } - - delete Chart.instances[this.id]; - }, - showTooltip : function(ChartElements, forceRedraw){ - // Only redraw the chart if we've actually changed what we're hovering on. - if (typeof this.activeElements === 'undefined') this.activeElements = []; - - var isChanged = (function(Elements){ - var changed = false; - - if (Elements.length !== this.activeElements.length){ - changed = true; - return changed; - } - - each(Elements, function(element, index){ - if (element !== this.activeElements[index]){ - changed = true; - } - }, this); - return changed; - }).call(this, ChartElements); - - if (!isChanged && !forceRedraw){ - return; - } - else{ - this.activeElements = ChartElements; - } - this.draw(); - if(this.options.customTooltips){ - this.options.customTooltips(false); - } - if (ChartElements.length > 0){ - // If we have multiple datasets, show a MultiTooltip for all of the data points at that index - if (this.datasets && this.datasets.length > 1) { - var dataArray, - dataIndex; - - for (var i = this.datasets.length - 1; i >= 0; i--) { - dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments; - dataIndex = indexOf(dataArray, ChartElements[0]); - if (dataIndex !== -1){ - break; - } - } - var tooltipLabels = [], - tooltipColors = [], - medianPosition = (function(index) { - - // Get all the points at that particular index - var Elements = [], - dataCollection, - xPositions = [], - yPositions = [], - xMax, - yMax, - xMin, - yMin; - helpers.each(this.datasets, function(dataset){ - dataCollection = dataset.points || dataset.bars || dataset.segments; - if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){ - Elements.push(dataCollection[dataIndex]); - } - }); - - helpers.each(Elements, function(element) { - xPositions.push(element.x); - yPositions.push(element.y); - - - //Include any colour information about the element - tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element)); - tooltipColors.push({ - fill: element._saved.fillColor || element.fillColor, - stroke: element._saved.strokeColor || element.strokeColor - }); - - }, this); - - yMin = min(yPositions); - yMax = max(yPositions); - - xMin = min(xPositions); - xMax = max(xPositions); - - return { - x: (xMin > this.chart.width/2) ? xMin : xMax, - y: (yMin + yMax)/2 - }; - }).call(this, dataIndex); - - new Chart.MultiTooltip({ - x: medianPosition.x, - y: medianPosition.y, - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - xOffset: this.options.tooltipXOffset, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - titleTextColor: this.options.tooltipTitleFontColor, - titleFontFamily: this.options.tooltipTitleFontFamily, - titleFontStyle: this.options.tooltipTitleFontStyle, - titleFontSize: this.options.tooltipTitleFontSize, - cornerRadius: this.options.tooltipCornerRadius, - labels: tooltipLabels, - legendColors: tooltipColors, - legendColorBackground : this.options.multiTooltipKeyBackground, - title: ChartElements[0].label, - chart: this.chart, - ctx: this.chart.ctx, - custom: this.options.customTooltips - }).draw(); - - } else { - each(ChartElements, function(Element) { - var tooltipPosition = Element.tooltipPosition(); - new Chart.Tooltip({ - x: Math.round(tooltipPosition.x), - y: Math.round(tooltipPosition.y), - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - caretHeight: this.options.tooltipCaretSize, - cornerRadius: this.options.tooltipCornerRadius, - text: template(this.options.tooltipTemplate, Element), - chart: this.chart, - custom: this.options.customTooltips - }).draw(); - }, this); - } - } - return this; - }, - toBase64Image : function(){ - return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); - } - }); - - Chart.Type.extend = function(extensions){ - - var parent = this; - - var ChartType = function(){ - return parent.apply(this,arguments); - }; - - //Copy the prototype object of the this class - ChartType.prototype = clone(parent.prototype); - //Now overwrite some of the properties in the base class with the new extensions - extend(ChartType.prototype, extensions); - - ChartType.extend = Chart.Type.extend; - - if (extensions.name || parent.prototype.name){ - - var chartName = extensions.name || parent.prototype.name; - //Assign any potential default values of the new chart type - - //If none are defined, we'll use a clone of the chart type this is being extended from. - //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart - //doesn't define some defaults of their own. - - var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {}; - - Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults); - - Chart.types[chartName] = ChartType; - - //Register this new chart type in the Chart prototype - Chart.prototype[chartName] = function(data,options){ - var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {}); - return new ChartType(data,config,this); - }; - } else{ - warn("Name not provided for this chart, so it hasn't been registered"); - } - return parent; - }; - - Chart.Element = function(configuration){ - extend(this,configuration); - this.initialize.apply(this,arguments); - this.save(); - }; - extend(Chart.Element.prototype,{ - initialize : function(){}, - restore : function(props){ - if (!props){ - extend(this,this._saved); - } else { - each(props,function(key){ - this[key] = this._saved[key]; - },this); - } - return this; - }, - save : function(){ - this._saved = clone(this); - delete this._saved._saved; - return this; - }, - update : function(newProps){ - each(newProps,function(value,key){ - this._saved[key] = this[key]; - this[key] = value; - },this); - return this; - }, - transition : function(props,ease){ - each(props,function(value,key){ - this[key] = ((value - this._saved[key]) * ease) + this._saved[key]; - },this); - return this; - }, - tooltipPosition : function(){ - return { - x : this.x, - y : this.y - }; - }, - hasValue: function(){ - return isNumber(this.value); - } - }); - - Chart.Element.extend = inherits; - - - Chart.Point = Chart.Element.extend({ - display: true, - inRange: function(chartX,chartY){ - var hitDetectionRange = this.hitDetectionRadius + this.radius; - return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2)); - }, - draw : function(){ - if (this.display){ - var ctx = this.ctx; - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); - ctx.closePath(); - - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.stroke(); - } - - - //Quick debug for bezier curve splining - //Highlights control points and the line between them. - //Handy for dev - stripped in the min version. - - // ctx.save(); - // ctx.fillStyle = "black"; - // ctx.strokeStyle = "black" - // ctx.beginPath(); - // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.beginPath(); - // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y); - // ctx.lineTo(this.x, this.y); - // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y); - // ctx.stroke(); - - // ctx.restore(); - - - - } - }); - - Chart.Arc = Chart.Element.extend({ - inRange : function(chartX,chartY){ - - var pointRelativePosition = helpers.getAngleFromPoint(this, { - x: chartX, - y: chartY - }); - - //Check if within the range of the open/close angle - var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle), - withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius); - - return (betweenAngles && withinRadius); - //Ensure within the outside of the arc centre, but inside arc outer - }, - tooltipPosition : function(){ - var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2), - rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius; - return { - x : this.x + (Math.cos(centreAngle) * rangeFromCentre), - y : this.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - draw : function(animationPercent){ - - var easingDecimal = animationPercent || 1; - - var ctx = this.ctx; - - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle); - - ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true); - - ctx.closePath(); - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.lineJoin = 'bevel'; - - if (this.showStroke){ - ctx.stroke(); - } - } - }); - - Chart.Rectangle = Chart.Element.extend({ - draw : function(){ - var ctx = this.ctx, - halfWidth = this.width/2, - leftX = this.x - halfWidth, - rightX = this.x + halfWidth, - top = this.base - (this.base - this.y), - halfStroke = this.strokeWidth / 2; - - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (this.showStroke){ - leftX += halfStroke; - rightX -= halfStroke; - top += halfStroke; - } - - ctx.beginPath(); - - ctx.fillStyle = this.fillColor; - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - // It'd be nice to keep this class totally generic to any rectangle - // and simply specify which border to miss out. - ctx.moveTo(leftX, this.base); - ctx.lineTo(leftX, top); - ctx.lineTo(rightX, top); - ctx.lineTo(rightX, this.base); - ctx.fill(); - if (this.showStroke){ - ctx.stroke(); - } - }, - height : function(){ - return this.base - this.y; - }, - inRange : function(chartX,chartY){ - return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base); - } - }); - - Chart.Tooltip = Chart.Element.extend({ - draw : function(){ - - var ctx = this.chart.ctx; - - ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.xAlign = "center"; - this.yAlign = "above"; - - //Distance between the actual element.y position and the start of the tooltip caret - var caretPadding = this.caretPadding = 2; - - var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, - tooltipRectHeight = this.fontSize + 2*this.yPadding, - tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding; - - if (this.x + tooltipWidth/2 >this.chart.width){ - this.xAlign = "left"; - } else if (this.x - tooltipWidth/2 < 0){ - this.xAlign = "right"; - } - - if (this.y - tooltipHeight < 0){ - this.yAlign = "below"; - } - - - var tooltipX = this.x - tooltipWidth/2, - tooltipY = this.y - tooltipHeight; - - ctx.fillStyle = this.fillColor; - - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - switch(this.yAlign) - { - case "above": - //Draw a caret above the x/y - ctx.beginPath(); - ctx.moveTo(this.x,this.y - caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.closePath(); - ctx.fill(); - break; - case "below": - tooltipY = this.y + caretPadding + this.caretHeight; - //Draw a caret below the x/y - ctx.beginPath(); - ctx.moveTo(this.x, this.y + caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.closePath(); - ctx.fill(); - break; - } - - switch(this.xAlign) - { - case "left": - tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); - break; - case "right": - tooltipX = this.x - (this.cornerRadius + this.caretHeight); - break; - } - - drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); - - ctx.fill(); - - ctx.fillStyle = this.textColor; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); - } - } - }); - - Chart.MultiTooltip = Chart.Element.extend({ - initialize : function(){ - this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily); - - this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5; - - this.ctx.font = this.titleFont; - - var titleWidth = this.ctx.measureText(this.title).width, - //Label has a legend square as well so account for this. - labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3, - longestTextWidth = max([labelWidth,titleWidth]); - - this.width = longestTextWidth + (this.xPadding*2); - - - var halfHeight = this.height/2; - - //Check to ensure the height will fit on the canvas - if (this.y - halfHeight < 0 ){ - this.y = halfHeight; - } else if (this.y + halfHeight > this.chart.height){ - this.y = this.chart.height - halfHeight; - } - - //Decide whether to align left or right based on position on canvas - if (this.x > this.chart.width/2){ - this.x -= this.xOffset + this.width; - } else { - this.x += this.xOffset; - } - - - }, - getLineHeight : function(index){ - var baseLineHeight = this.y - (this.height/2) + this.yPadding, - afterTitleIndex = index-1; - - //If the index is zero, we're getting the title - if (index === 0){ - return baseLineHeight + this.titleFontSize/2; - } else{ - return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5; - } - - }, - draw : function(){ - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); - var ctx = this.ctx; - ctx.fillStyle = this.fillColor; - ctx.fill(); - ctx.closePath(); - - ctx.textAlign = "left"; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.titleTextColor; - ctx.font = this.titleFont; - - ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); - - ctx.font = this.font; - helpers.each(this.labels,function(label,index){ - ctx.fillStyle = this.textColor; - ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); - - //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) - //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - //Instead we'll make a white filled block to put the legendColour palette over. - - ctx.fillStyle = this.legendColorBackground; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - ctx.fillStyle = this.legendColors[index].fill; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - - },this); - } - } - }); - - Chart.Scale = Chart.Element.extend({ - initialize : function(){ - this.fit(); - }, - buildYLabels : function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0; - }, - addXLabel : function(label){ - this.xLabels.push(label); - this.valuesCount++; - this.fit(); - }, - removeXLabel : function(){ - this.xLabels.shift(); - this.valuesCount--; - this.fit(); - }, - // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use - fit: function(){ - // First we need the width of the yLabels, assuming the xLabels aren't rotated - - // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation - this.startPoint = (this.display) ? this.fontSize : 0; - this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels - - // Apply padding settings to the start and end point. - this.startPoint += this.padding; - this.endPoint -= this.padding; - - // Cache the starting height, so can determine if we need to recalculate the scale yAxis - var cachedHeight = this.endPoint - this.startPoint, - cachedYLabelWidth; - - // Build the current yLabels so we have an idea of what size they'll be to start - /* - * This sets what is returned from calculateScaleRange as static properties of this class: - * - this.steps; - this.stepValue; - this.min; - this.max; - * - */ - this.calculateYRange(cachedHeight); - - // With these properties set we can now build the array of yLabels - // and also the width of the largest yLabel - this.buildYLabels(); - - this.calculateXLabelRotation(); - - while((cachedHeight > this.endPoint - this.startPoint)){ - cachedHeight = this.endPoint - this.startPoint; - cachedYLabelWidth = this.yLabelWidth; - - this.calculateYRange(cachedHeight); - this.buildYLabels(); - - // Only go through the xLabel loop again if the yLabel width has changed - if (cachedYLabelWidth < this.yLabelWidth){ - this.calculateXLabelRotation(); - } - } - - }, - calculateXLabelRotation : function(){ - //Get the width of each grid by calculating the difference - //between x offsets between 0 and 1. - - this.ctx.font = this.font; - - var firstWidth = this.ctx.measureText(this.xLabels[0]).width, - lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width, - firstRotated, - lastRotated; - - - this.xScalePaddingRight = lastWidth/2 + 3; - this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10; - - this.xLabelRotation = 0; - if (this.display){ - var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels), - cosRotation, - firstRotatedWidth; - this.xLabelWidth = originalLabelWidth; - //Allow 3 pixels x2 padding either side for label readability - var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6; - - //Max label rotate should be 90 - also act as a loop counter - while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){ - cosRotation = Math.cos(toRadians(this.xLabelRotation)); - - firstRotated = cosRotation * firstWidth; - lastRotated = cosRotation * lastWidth; - - // We're right aligning the text now. - if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){ - this.xScalePaddingLeft = firstRotated + this.fontSize / 2; - } - this.xScalePaddingRight = this.fontSize/2; - - - this.xLabelRotation++; - this.xLabelWidth = cosRotation * originalLabelWidth; - - } - if (this.xLabelRotation > 0){ - this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3; - } - } - else{ - this.xLabelWidth = 0; - this.xScalePaddingRight = this.padding; - this.xScalePaddingLeft = this.padding; - } - - }, - // Needs to be overidden in each Chart type - // Otherwise we need to pass all the data into the scale class - calculateYRange: noop, - drawingArea: function(){ - return this.startPoint - this.endPoint; - }, - calculateY : function(value){ - var scalingFactor = this.drawingArea() / (this.min - this.max); - return this.endPoint - (scalingFactor * (value - this.min)); - }, - calculateX : function(index){ - var isRotated = (this.xLabelRotation > 0), - // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding, - innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight), - valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1), - valueOffset = (valueWidth * index) + this.xScalePaddingLeft; - - if (this.offsetGridLines){ - valueOffset += (valueWidth/2); - } - - return Math.round(valueOffset); - }, - update : function(newProps){ - helpers.extend(this, newProps); - this.fit(); - }, - draw : function(){ - var ctx = this.ctx, - yLabelGap = (this.endPoint - this.startPoint) / this.steps, - xStart = Math.round(this.xScalePaddingLeft); - if (this.display){ - ctx.fillStyle = this.textColor; - ctx.font = this.font; - each(this.yLabels,function(labelString,index){ - var yLabelCenter = this.endPoint - (yLabelGap * index), - linePositionY = Math.round(yLabelCenter), - drawHorizontalLine = this.showHorizontalLines; - - ctx.textAlign = "right"; - ctx.textBaseline = "middle"; - if (this.showLabels){ - ctx.fillText(labelString,xStart - 10,yLabelCenter); - } - - // This is X axis, so draw it - if (index === 0 && !drawHorizontalLine){ - drawHorizontalLine = true; - } - - if (drawHorizontalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - linePositionY += helpers.aliasPixel(ctx.lineWidth); - - if(drawHorizontalLine){ - ctx.moveTo(xStart, linePositionY); - ctx.lineTo(this.width, linePositionY); - ctx.stroke(); - ctx.closePath(); - } - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - ctx.beginPath(); - ctx.moveTo(xStart - 5, linePositionY); - ctx.lineTo(xStart, linePositionY); - ctx.stroke(); - ctx.closePath(); - - },this); - - each(this.xLabels,function(label,index){ - var xPos = this.calculateX(index) + aliasPixel(this.lineWidth), - // Check to see if line/bar here and decide where to place the line - linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth), - isRotated = (this.xLabelRotation > 0), - drawVerticalLine = this.showVerticalLines; - - // This is Y axis, so draw it - if (index === 0 && !drawVerticalLine){ - drawVerticalLine = true; - } - - if (drawVerticalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - if (drawVerticalLine){ - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.startPoint - 3); - ctx.stroke(); - ctx.closePath(); - } - - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - - - // Small lines at the bottom of the base grid line - ctx.beginPath(); - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.endPoint + 5); - ctx.stroke(); - ctx.closePath(); - - ctx.save(); - ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8); - ctx.rotate(toRadians(this.xLabelRotation)*-1); - ctx.font = this.font; - ctx.textAlign = (isRotated) ? "right" : "center"; - ctx.textBaseline = (isRotated) ? "middle" : "top"; - ctx.fillText(label, 0, 0); - ctx.restore(); - },this); - - } - } - - }); - - Chart.RadialScale = Chart.Element.extend({ - initialize: function(){ - this.size = min([this.height, this.width]); - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - }, - calculateCenterOffset: function(value){ - // Take into account half font size + the yPadding of the top value - var scalingFactor = this.drawingArea / (this.max - this.min); - - return (value - this.min) * scalingFactor; - }, - update : function(){ - if (!this.lineArc){ - this.setScaleSize(); - } else { - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - } - this.buildYLabels(); - }, - buildYLabels: function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - }, - getCircumference : function(){ - return ((Math.PI*2) / this.valuesCount); - }, - setScaleSize: function(){ - /* - * Right, this is really confusing and there is a lot of maths going on here - * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - * - * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - * - * Solution: - * - * We assume the radius of the polygon is half the size of the canvas at first - * at each index we check if the text overlaps. - * - * Where it does, we store that angle and that index. - * - * After finding the largest index and angle we calculate how much we need to remove - * from the shape radius to move the point inwards by that x. - * - * We average the left and right distances to get the maximum shape radius that can fit in the box - * along with labels. - * - * Once we have that, we can find the centre point for the chart, by taking the x text protrusion - * on each side, removing that from the size, halving it and adding the left x protrusion width. - * - * This will mean we have a shape fitted to the canvas, as large as it can be with the labels - * and position it in the most space efficient manner - * - * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - */ - - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]), - pointPosition, - i, - textWidth, - halfTextWidth, - furthestRight = this.width, - furthestRightIndex, - furthestRightAngle, - furthestLeft = 0, - furthestLeftIndex, - furthestLeftAngle, - xProtrusionLeft, - xProtrusionRight, - radiusReductionRight, - radiusReductionLeft, - maxWidthRadius; - this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - for (i=0;i furthestRight) { - furthestRight = pointPosition.x + halfTextWidth; - furthestRightIndex = i; - } - if (pointPosition.x - halfTextWidth < furthestLeft) { - furthestLeft = pointPosition.x - halfTextWidth; - furthestLeftIndex = i; - } - } - else if (i < this.valuesCount/2) { - // Less than half the values means we'll left align the text - if (pointPosition.x + textWidth > furthestRight) { - furthestRight = pointPosition.x + textWidth; - furthestRightIndex = i; - } - } - else if (i > this.valuesCount/2){ - // More than half the values means we'll right align the text - if (pointPosition.x - textWidth < furthestLeft) { - furthestLeft = pointPosition.x - textWidth; - furthestLeftIndex = i; - } - } - } - - xProtrusionLeft = furthestLeft; - - xProtrusionRight = Math.ceil(furthestRight - this.width); - - furthestRightAngle = this.getIndexAngle(furthestRightIndex); - - furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); - - radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2); - - radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2); - - // Ensure we actually need to reduce the size of the chart - radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0; - radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; - - this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2; - - //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2]) - this.setCenterPoint(radiusReductionLeft, radiusReductionRight); - - }, - setCenterPoint: function(leftMovement, rightMovement){ - - var maxRight = this.width - rightMovement - this.drawingArea, - maxLeft = leftMovement + this.drawingArea; - - this.xCenter = (maxLeft + maxRight)/2; - // Always vertically in the centre as the text height doesn't change - this.yCenter = (this.height/2); - }, - - getIndexAngle : function(index){ - var angleMultiplier = (Math.PI * 2) / this.valuesCount; - // Start from the top instead of right, so remove a quarter of the circle - - return index * angleMultiplier - (Math.PI/2); - }, - getPointPosition : function(index, distanceFromCenter){ - var thisAngle = this.getIndexAngle(index); - return { - x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter, - y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter - }; - }, - draw: function(){ - if (this.display){ - var ctx = this.ctx; - each(this.yLabels, function(label, index){ - // Don't draw a centre value - if (index > 0){ - var yCenterOffset = index * (this.drawingArea/this.steps), - yHeight = this.yCenter - yCenterOffset, - pointPosition; - - // Draw circular lines around the scale - if (this.lineWidth > 0){ - ctx.strokeStyle = this.lineColor; - ctx.lineWidth = this.lineWidth; - - if(this.lineArc){ - ctx.beginPath(); - ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2); - ctx.closePath(); - ctx.stroke(); - } else{ - ctx.beginPath(); - for (var i=0;i= 0; i--) { - if (this.angleLineWidth > 0){ - var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max)); - ctx.beginPath(); - ctx.moveTo(this.xCenter, this.yCenter); - ctx.lineTo(outerPosition.x, outerPosition.y); - ctx.stroke(); - ctx.closePath(); - } - // Extra 3px out for some label spacing - var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5); - ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - ctx.fillStyle = this.pointLabelFontColor; - - var labelsCount = this.labels.length, - halfLabelsCount = this.labels.length/2, - quarterLabelsCount = halfLabelsCount/2, - upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), - exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); - if (i === 0){ - ctx.textAlign = 'center'; - } else if(i === halfLabelsCount){ - ctx.textAlign = 'center'; - } else if (i < halfLabelsCount){ - ctx.textAlign = 'left'; - } else { - ctx.textAlign = 'right'; - } - - // Set the correct text baseline based on outer positioning - if (exactQuarter){ - ctx.textBaseline = 'middle'; - } else if (upperHalf){ - ctx.textBaseline = 'bottom'; - } else { - ctx.textBaseline = 'top'; - } - - ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y); - } - } - } - } - }); - - // Attach global event to resize each chart instance when the browser resizes - helpers.addEvent(window, "resize", (function(){ - // Basic debounce of resize function so it doesn't hurt performance when resizing browser. - var timeout; - return function(){ - clearTimeout(timeout); - timeout = setTimeout(function(){ - each(Chart.instances,function(instance){ - // If the responsive flag is set in the chart instance config - // Cascade the resize event down to the chart. - if (instance.options.responsive){ - instance.resize(instance.render, true); - } - }); - }, 50); - }; - })()); - - - if (amd) { - define(function(){ - return Chart; - }); - } else if (typeof module === 'object' && module.exports) { - module.exports = Chart; - } - - root.Chart = Chart; - - Chart.noConflict = function(){ - root.Chart = previous; - return Chart; - }; - -}).call(this); diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.Doughnut.js b/web/backend/resources/bower_components/chart.js/src/Chart.Doughnut.js deleted file mode 100644 index a892b0efc..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.Doughnut.js +++ /dev/null @@ -1,184 +0,0 @@ -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke : true, - - //String - The colour of each segment stroke - segmentStrokeColor : "#fff", - - //Number - The width of each segment stroke - segmentStrokeWidth : 2, - - //The percentage of the chart that we cut out of the middle. - percentageInnerCutout : 50, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect - animationEasing : "easeOutBounce", - - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate : true, - - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    " - - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "Doughnut", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - - //Declare segments as a static property to prevent inheriting across the Chart type prototype - this.segments = []; - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - - this.SegmentArc = Chart.Arc.extend({ - ctx : this.chart.ctx, - x : this.chart.width/2, - y : this.chart.height/2 - }); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - this.calculateTotal(data); - - helpers.each(data,function(datapoint, index){ - this.addData(datapoint, index, true); - },this); - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - this.segments.splice(index, 0, new this.SegmentArc({ - value : segment.value, - outerRadius : (this.options.animateScale) ? 0 : this.outerRadius, - innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout, - fillColor : segment.color, - highlightColor : segment.highlight || segment.color, - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - startAngle : Math.PI * 1.5, - circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), - label : segment.label - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - calculateCircumference : function(value){ - return (Math.PI*2)*(Math.abs(value) / this.total); - }, - calculateTotal : function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += Math.abs(segment.value); - },this); - }, - update : function(){ - this.calculateTotal(this.segments); - - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor']); - }); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - this.render(); - }, - - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - }); - }, this); - }, - draw : function(easeDecimal){ - var animDecimal = (easeDecimal) ? easeDecimal : 1; - this.clear(); - helpers.each(this.segments,function(segment,index){ - segment.transition({ - circumference : this.calculateCircumference(segment.value), - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - },animDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - segment.draw(); - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length-1){ - this.segments[index+1].startAngle = segment.endAngle; - } - },this); - - } - }); - - Chart.types.Doughnut.extend({ - name : "Pie", - defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0}) - }); - -}).call(this); \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.Line.js b/web/backend/resources/bower_components/chart.js/src/Chart.Line.js deleted file mode 100644 index 34ad85b4d..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.Line.js +++ /dev/null @@ -1,374 +0,0 @@ -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - var defaultConfig = { - - ///Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - Whether the line is curved between points - bezierCurve : true, - - //Number - Tension of the bezier curve between points - bezierCurveTension : 0.4, - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 4, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - - }; - - - Chart.Type.extend({ - name: "Line", - defaults : defaultConfig, - initialize: function(data){ - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx, - inRange : function(mouseX){ - return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2)); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePoints, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - this.showTooltip(activePoints); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - this.buildScale(data.labels); - - - this.eachPoints(function(point, index){ - helpers.extend(point, { - x: this.scale.calculateX(index), - y: this.scale.endPoint - }); - point.save(); - }, this); - - },this); - - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - this.eachPoints(function(point){ - point.save(); - }); - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - getPointsAtEvent : function(e){ - var pointsArray = [], - eventPosition = helpers.getRelativePosition(e); - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,function(point){ - if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point); - }); - },this); - return pointsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachPoints(function(point){ - values.push(point.value); - }); - - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange : function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - - this.scale = new Chart.Scale(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: this.scale.calculateX(this.scale.valuesCount+1), - y: this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.update(); - }, - reflow : function(){ - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - // Some helper methods for getting the next/prev points - var hasValue = function(item){ - return item.value !== null; - }, - nextPoint = function(point, collection, index){ - return helpers.findNextWhere(collection, hasValue, index) || point; - }, - previousPoint = function(point, collection, index){ - return helpers.findPreviousWhere(collection, hasValue, index) || point; - }; - - this.scale.draw(easingDecimal); - - - helpers.each(this.datasets,function(dataset){ - var pointsWithValues = helpers.where(dataset.points, hasValue); - - //Transition each point first so that the line and point drawing isn't out of sync - //We can use this extra loop to calculate the control points of this dataset also in this loop - - helpers.each(dataset.points, function(point, index){ - if (point.hasValue()){ - point.transition({ - y : this.scale.calculateY(point.value), - x : this.scale.calculateX(index) - }, easingDecimal); - } - },this); - - - // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point - // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed - if (this.options.bezierCurve){ - helpers.each(pointsWithValues, function(point, index){ - var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0; - point.controlPoints = helpers.splineCurve( - previousPoint(point, pointsWithValues, index), - point, - nextPoint(point, pointsWithValues, index), - tension - ); - - // Prevent the bezier going outside of the bounds of the graph - - // Cap puter bezier handles to the upper/lower scale bounds - if (point.controlPoints.outer.y > this.scale.endPoint){ - point.controlPoints.outer.y = this.scale.endPoint; - } - else if (point.controlPoints.outer.y < this.scale.startPoint){ - point.controlPoints.outer.y = this.scale.startPoint; - } - - // Cap inner bezier handles to the upper/lower scale bounds - if (point.controlPoints.inner.y > this.scale.endPoint){ - point.controlPoints.inner.y = this.scale.endPoint; - } - else if (point.controlPoints.inner.y < this.scale.startPoint){ - point.controlPoints.inner.y = this.scale.startPoint; - } - },this); - } - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - - helpers.each(pointsWithValues, function(point, index){ - if (index === 0){ - ctx.moveTo(point.x, point.y); - } - else{ - if(this.options.bezierCurve){ - var previous = previousPoint(point, pointsWithValues, index); - - ctx.bezierCurveTo( - previous.controlPoints.outer.x, - previous.controlPoints.outer.y, - point.controlPoints.inner.x, - point.controlPoints.inner.y, - point.x, - point.y - ); - } - else{ - ctx.lineTo(point.x,point.y); - } - } - }, this); - - ctx.stroke(); - - if (this.options.datasetFill && pointsWithValues.length > 0){ - //Round off the line by going to the base of the chart, back to the start, then fill. - ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint); - ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint); - ctx.fillStyle = dataset.fillColor; - ctx.closePath(); - ctx.fill(); - } - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(pointsWithValues,function(point){ - point.draw(); - }); - },this); - } - }); - - -}).call(this); diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.PolarArea.js b/web/backend/resources/bower_components/chart.js/src/Chart.PolarArea.js deleted file mode 100644 index 8f2b9c52c..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.PolarArea.js +++ /dev/null @@ -1,250 +0,0 @@ -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Show a backdrop to the scale label - scaleShowLabelBackdrop : true, - - //String - The colour of the label backdrop - scaleBackdropColor : "rgba(255,255,255,0.75)", - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //Number - The backdrop padding above & below the label in pixels - scaleBackdropPaddingY : 2, - - //Number - The backdrop padding to the side of the label in pixels - scaleBackdropPaddingX : 2, - - //Boolean - Show line for each value in the scale - scaleShowLine : true, - - //Boolean - Stroke a line around each segment in the chart - segmentShowStroke : true, - - //String - The colour of the stroke on each segement. - segmentStrokeColor : "#fff", - - //Number - The width of the stroke value in pixels - segmentStrokeWidth : 2, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect. - animationEasing : "easeOutBounce", - - //Boolean - Whether to animate the rotation of the chart - animateRotate : true, - - //Boolean - Whether to animate scaling the chart from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    " - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "PolarArea", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - this.segments = []; - //Declare segment class as a chart instance specific class, so it can share props for this instance - this.SegmentArc = Chart.Arc.extend({ - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - ctx : this.chart.ctx, - innerRadius : 0, - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - lineArc: true, - width: this.chart.width, - height: this.chart.height, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - valuesCount: data.length - }); - - this.updateScaleRange(data); - - this.scale.update(); - - helpers.each(data,function(segment,index){ - this.addData(segment,index,true); - },this); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - - this.segments.splice(index, 0, new this.SegmentArc({ - fillColor: segment.color, - highlightColor: segment.highlight || segment.color, - label: segment.label, - value: segment.value, - outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value), - circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(), - startAngle: Math.PI * 1.5 - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - calculateTotal: function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += segment.value; - },this); - this.scale.valuesCount = this.segments.length; - }, - updateScaleRange: function(datapoints){ - var valuesArray = []; - helpers.each(datapoints,function(segment){ - valuesArray.push(segment.value); - }); - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes, - { - size: helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - } - ); - - }, - update : function(){ - this.calculateTotal(this.segments); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - - this.reflow(); - this.render(); - }, - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.updateScaleRange(this.segments); - this.scale.update(); - - helpers.extend(this.scale,{ - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.scale.calculateCenterOffset(segment.value) - }); - }, this); - - }, - draw : function(ease){ - var easingDecimal = ease || 1; - //Clear & draw the canvas - this.clear(); - helpers.each(this.segments,function(segment, index){ - segment.transition({ - circumference : this.scale.getCircumference(), - outerRadius : this.scale.calculateCenterOffset(segment.value) - },easingDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - // If we've removed the first segment we need to set the first one to - // start at the top. - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length - 1){ - this.segments[index+1].startAngle = segment.endAngle; - } - segment.draw(); - }, this); - this.scale.draw(); - } - }); - -}).call(this); \ No newline at end of file diff --git a/web/backend/resources/bower_components/chart.js/src/Chart.Radar.js b/web/backend/resources/bower_components/chart.js/src/Chart.Radar.js deleted file mode 100644 index 134fd2d02..000000000 --- a/web/backend/resources/bower_components/chart.js/src/Chart.Radar.js +++ /dev/null @@ -1,343 +0,0 @@ -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - - Chart.Type.extend({ - name: "Radar", - defaults:{ - //Boolean - Whether to show lines for each scale point - scaleShowLine : true, - - //Boolean - Whether we show the angle lines out of the radar - angleShowLineOut : true, - - //Boolean - Whether to show labels on the scale - scaleShowLabels : false, - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //String - Colour of the angle line - angleLineColor : "rgba(0,0,0,.1)", - - //Number - Pixel width of the angle line - angleLineWidth : 1, - - //String - Point label font declaration - pointLabelFontFamily : "'Arial'", - - //String - Point label font weight - pointLabelFontStyle : "normal", - - //Number - Point label font size in pixels - pointLabelFontSize : 10, - - //String - Point label font colour - pointLabelFontColor : "#666", - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 3, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "
      -legend\"><% for (var i=0; i
    • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    " - - }, - - initialize: function(data){ - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx - }); - - this.datasets = []; - - this.buildScale(data); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePointsCollection, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - - this.showTooltip(activePointsCollection); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label: dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - var pointPosition; - if (!this.scale.animation){ - pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint)); - } - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - x: (this.options.animation) ? this.scale.xCenter : pointPosition.x, - y: (this.options.animation) ? this.scale.yCenter : pointPosition.y, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - },this); - - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - - getPointsAtEvent : function(evt){ - var mousePosition = helpers.getRelativePosition(evt), - fromCenter = helpers.getAngleFromPoint({ - x: this.scale.xCenter, - y: this.scale.yCenter - }, mousePosition); - - var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount, - pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex), - activePointsCollection = []; - - // If we're at the top, make the pointIndex 0 to get the first of the array. - if (pointIndex >= this.scale.valuesCount || pointIndex < 0){ - pointIndex = 0; - } - - if (fromCenter.distance <= this.scale.drawingArea){ - helpers.each(this.datasets, function(dataset){ - activePointsCollection.push(dataset.points[pointIndex]); - }); - } - - return activePointsCollection; - }, - - buildScale : function(data){ - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - angleLineColor : this.options.angleLineColor, - angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0, - // Point labels at the edge of each line - pointLabelFontColor : this.options.pointLabelFontColor, - pointLabelFontSize : this.options.pointLabelFontSize, - pointLabelFontFamily : this.options.pointLabelFontFamily, - pointLabelFontStyle : this.options.pointLabelFontStyle, - height : this.chart.height, - width: this.chart.width, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - labels: data.labels, - valuesCount: data.datasets[0].data.length - }); - - this.scale.setScaleSize(); - this.updateScaleRange(data.datasets); - this.scale.buildYLabels(); - }, - updateScaleRange: function(datasets){ - var valuesArray = (function(){ - var totalDataArray = []; - helpers.each(datasets,function(dataset){ - if (dataset.data){ - totalDataArray = totalDataArray.concat(dataset.data); - } - else { - helpers.each(dataset.points, function(point){ - totalDataArray.push(point.value); - }); - } - }); - return totalDataArray; - })(); - - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes - ); - - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - this.scale.valuesCount++; - helpers.each(valuesArray,function(value,datasetIndex){ - var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value)); - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: pointPosition.x, - y: pointPosition.y, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.labels.push(label); - - this.reflow(); - - this.update(); - }, - removeData : function(){ - this.scale.valuesCount--; - this.scale.labels.shift(); - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.reflow(); - this.update(); - }, - update : function(){ - this.eachPoints(function(point){ - point.save(); - }); - this.reflow(); - this.render(); - }, - reflow: function(){ - helpers.extend(this.scale, { - width : this.chart.width, - height: this.chart.height, - size : helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - this.updateScaleRange(this.datasets); - this.scale.setScaleSize(); - this.scale.buildYLabels(); - }, - draw : function(ease){ - var easeDecimal = ease || 1, - ctx = this.chart.ctx; - this.clear(); - this.scale.draw(); - - helpers.each(this.datasets,function(dataset){ - - //Transition each point first so that the line and point drawing isn't out of sync - helpers.each(dataset.points,function(point,index){ - if (point.hasValue()){ - point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal); - } - },this); - - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - helpers.each(dataset.points,function(point,index){ - if (index === 0){ - ctx.moveTo(point.x,point.y); - } - else{ - ctx.lineTo(point.x,point.y); - } - },this); - ctx.closePath(); - ctx.stroke(); - - ctx.fillStyle = dataset.fillColor; - ctx.fill(); - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(dataset.points,function(point){ - if (point.hasValue()){ - point.draw(); - } - }); - - },this); - - } - - }); - - - - - -}).call(this); \ No newline at end of file diff --git a/web/backend/resources/dist/css/checkbox.css b/web/backend/resources/dist/css/checkbox.css new file mode 100644 index 000000000..06587fd53 --- /dev/null +++ b/web/backend/resources/dist/css/checkbox.css @@ -0,0 +1,257 @@ + +/* +复选框+单选 +官网地址:https://www.bootcdn.cn/awesome-bootstrap-checkbox/#about +*/ +.checkbox { + padding-left: 20px; +} + +.checkbox label { + display: inline-block; + position: relative; + padding-left: 5px; +} + +.checkbox label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 3px; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + transition: border 0.15s ease-in-out, color 0.15s ease-in-out; +} + +.checkbox label::after { + display: inline-block; + position: absolute; + width: 16px; + height: 16px; + left: 0; + top: 0; + margin-left: -20px; + padding-left: 3.5px; + padding-top: 0; + font-size: 12px; + color: #555555; +} + +.checkbox input[type="checkbox"] { + opacity: 0; +} + +.checkbox input[type="checkbox"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.checkbox input[type="checkbox"]:checked + label::after { + font-family: 'Ionicons'; + content: "\f121"; +} + +.checkbox input[type="checkbox"]:disabled + label { + opacity: 0.65; +} + +.checkbox input[type="checkbox"]:disabled + label::before { + background-color: #eeeeee; + cursor: not-allowed; +} + +.checkbox.checkbox-circle label::before { + border-radius: 50%; +} + +.checkbox.checkbox-inline { + margin-top: 0; +} + +.checkbox-primary input[type="checkbox"]:checked + label::before { + background-color: #3e8ef7; + border-color: #3e8ef7; +} + +.checkbox-primary input[type="checkbox"]:checked + label::after { + color: #fff; +} + +.checkbox-danger input[type="checkbox"]:checked + label::before { + background-color: #ff4c52; + border-color: #ff4c52; +} + +.checkbox-danger input[type="checkbox"]:checked + label::after { + color: #fff; +} + +.checkbox-info input[type="checkbox"]:checked + label::before { + background-color: #0bb2d4; + border-color: #0bb2d4; +} + +.checkbox-info input[type="checkbox"]:checked + label::after { + color: #fff; +} + +.checkbox-warning input[type="checkbox"]:checked + label::before { + background-color: #eb6709; + border-color: #eb6709; +} + +.checkbox-warning input[type="checkbox"]:checked + label::after { + color: #fff; +} + +.checkbox-success input[type="checkbox"]:checked + label::before { + background-color: #11c26d; + border-color: #11c26d; +} + +.checkbox-success input[type="checkbox"]:checked + label::after { + color: #fff; +} + +.radio { + padding-left: 20px; +} + +.radio label { + display: inline-block; + position: relative; + padding-left: 5px; +} + +.radio label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 50%; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out; + transition: border 0.15s ease-in-out; +} + +.radio label::after { + display: inline-block; + position: absolute; + content: " "; + width: 11px; + height: 11px; + left: 3px; + top: 3px; + margin-left: -20px; + border-radius: 50%; + background-color: #555555; + -webkit-transform: scale(0, 0); + -ms-transform: scale(0, 0); + -o-transform: scale(0, 0); + transform: scale(0, 0); + -webkit-transition: -webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -moz-transition: -moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -o-transition: -o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + transition: transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); +} + +.radio input[type="radio"] { + opacity: 0; +} + +.radio input[type="radio"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.radio input[type="radio"]:checked + label::after { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} + +.radio input[type="radio"]:disabled + label { + opacity: 0.65; +} + +.radio input[type="radio"]:disabled + label::before { + cursor: not-allowed; +} + +.radio.radio-inline { + margin-top: 0; +} + +.radio-primary input[type="radio"] + label::after { + background-color: #3e8ef7; +} + +.radio-primary input[type="radio"]:checked + label::before { + border-color: #3e8ef7; +} + +.radio-primary input[type="radio"]:checked + label::after { + background-color: #3e8ef7; +} + +.radio-danger input[type="radio"] + label::after { + background-color: #ff4c52; +} + +.radio-danger input[type="radio"]:checked + label::before { + border-color: #ff4c52; +} + +.radio-danger input[type="radio"]:checked + label::after { + background-color: #ff4c52; +} + +.radio-info input[type="radio"] + label::after { + background-color: #0bb2d4; +} + +.radio-info input[type="radio"]:checked + label::before { + border-color: #0bb2d4; +} + +.radio-info input[type="radio"]:checked + label::after { + background-color: #0bb2d4; +} + +.radio-warning input[type="radio"] + label::after { + background-color: #eb6709; +} + +.radio-warning input[type="radio"]:checked + label::before { + border-color: #eb6709; +} + +.radio-warning input[type="radio"]:checked + label::after { + background-color: #eb6709; +} + +.radio-success input[type="radio"] + label::after { + background-color: #11c26d; +} + +.radio-success input[type="radio"]:checked + label::before { + border-color: #11c26d; +} + +.radio-success input[type="radio"]:checked + label::after { + background-color: #11c26d; +} \ No newline at end of file diff --git a/web/backend/resources/dist/css/rageframe.css b/web/backend/resources/dist/css/rageframe.css index 49b0b24c0..0c0f8962a 100644 --- a/web/backend/resources/dist/css/rageframe.css +++ b/web/backend/resources/dist/css/rageframe.css @@ -110,6 +110,10 @@ margin-top: 2px; } +.desk-menu { + margin-bottom: 0; +} + /* ====== 替换AdminLTE样式 ====== */ a { /*color: #589ffc;*/ @@ -884,7 +888,7 @@ div.required label:before { } /*列表 必填项 取消加星*/ -div.required input+div label:before { +div.required input + div label:before { content: ""; color: red; } @@ -1061,6 +1065,14 @@ nav.page-tabs .page-tabs-content { /* 内边距 & 外边距 */ +.p-r-no-away { + padding-right: 0; +} + +.p-l-no-away { + padding-left: 0; +} + .p-xxs { padding: 5px; } diff --git a/web/backend/resources/dist/css/rageframe.widgets.css b/web/backend/resources/dist/css/rageframe.widgets.css index f8df6d10c..1c5d4d235 100644 --- a/web/backend/resources/dist/css/rageframe.widgets.css +++ b/web/backend/resources/dist/css/rageframe.widgets.css @@ -43,6 +43,7 @@ opacity: 1; } +.upload-list ul li .img-box:hover .delimg-crop, .upload-list ul li .img-box:hover .delimg { position: absolute; width: 25px; diff --git a/web/backend/resources/dist/js/contabs.js b/web/backend/resources/dist/js/contabs.js index 22d8fa745..3e16b5299 100644 --- a/web/backend/resources/dist/js/contabs.js +++ b/web/backend/resources/dist/js/contabs.js @@ -187,6 +187,9 @@ $(function () { return false; } }); + + // 重新加载 + $(this).trigger('dblclick'); } flag = false; return false; diff --git a/web/backend/resources/dist/js/rageframe.js b/web/backend/resources/dist/js/rageframe.js index 513e41eb0..f03a7fd02 100644 --- a/web/backend/resources/dist/js/rageframe.js +++ b/web/backend/resources/dist/js/rageframe.js @@ -140,6 +140,7 @@ $(document).on("click", ".openIframe", function(e){ var title = $(this).data('title'); var width = $(this).data('width'); var height = $(this).data('height'); + var offset = $(this).data('offset'); var href = $(this).attr('href'); if (title == undefined) { @@ -154,7 +155,11 @@ $(document).on("click", ".openIframe", function(e){ height = '80%'; } - openIframe(title, width, height, href); + if (offset == undefined) { + offset = "10%"; + } + + openIframe(title, width, height, href, offset); e.preventDefault(); return false; }); @@ -164,12 +169,12 @@ layer.config({ }); // 打一个新窗口 -function openIframe(title, width, height, content){ +function openIframe(title, width, height, content, offset){ layer.open({ type: 2, title: title, shade: 0.3, - offset: "10%", + offset: offset, shadeClose : true, btn: ['保存', '关闭'], yes: function(index, layero) { diff --git a/web/backend/resources/dist/js/rageframe.widgets.js b/web/backend/resources/dist/js/rageframe.widgets.js index 887d8eee7..3052bda6a 100644 --- a/web/backend/resources/dist/js/rageframe.widgets.js +++ b/web/backend/resources/dist/js/rageframe.widgets.js @@ -1,4 +1,3 @@ - let echartsList = []; let echartsListConfig = []; // ----------------------------------- 文件上传 ----------------------------------- // @@ -37,6 +36,14 @@ $(document).on("click", ".upload-box", function (e) { }); // 触发上传2 +$(document).on("click", ".upload-box .fa-cloud-upload", function (e) { + if (e.target == this) { + let boxId = $(this).parent().parent().attr('data-boxId'); + $('#upload-' + boxId + ' .webuploader-container input').trigger('click'); + } +}); + +// 触发上传3 $(document).on("click", ".upload-box-immediately", function () { let boxId = $(this).parent().parent().parent().attr('data-boxId'); $('#upload-' + boxId + ' .webuploader-container input').trigger('click'); @@ -55,6 +62,7 @@ $(function () { let uploader = WebUploader.create(config); //当validate不通过时,会以派送错误事件的形式通知 uploader.on('error', function (type) { + console.log(type); switch (type) { case 'Q_EXCEED_NUM_LIMIT': rfError("上传文件数量过多!"); @@ -66,7 +74,7 @@ $(function () { rfError("文件大小超出限制!"); break; case 'Q_TYPE_DENIED': - rfError("禁止上传该类型文件!"); + rfError("禁止上传该类型文件!", '请检查文件类型或文件为空文件'); break; case 'F_DUPLICATE': rfError("请勿重复上传该文件!"); @@ -131,7 +139,7 @@ $(function () { console.log(data); // 阿里js直传 if (config.formData.drive == 'oss-js') { - getOssPathUrl(data, config); + getOssPathUrl(data, config, file); return; } @@ -232,13 +240,13 @@ $(function () { } // 获取oss js直传的文件信息 - function getOssPathUrl(data, config) { + function getOssPathUrl(data, config, file) { var url = $(data._raw).find("Location").text(); $.ajax({ type: "post", url: config.getOssPathUrl, dataType: "json", - data: {url: url, type: config.type}, + data: {url: url, type: config.type, md5: md5[file.id]}, success: function (data) { if (parseInt(data.code) === 200) { data = data.data; @@ -352,4 +360,107 @@ $(document).on("click", ".map-select", function (e) { url = url + "&lat=" + lat; parent.find('.rfSelectMap').attr('href', url); parent.find('.rfSelectMap').trigger('click') -}); \ No newline at end of file +}); + +// ----------------------------------- JsTree ----------------------------------- // + +/** + * 带checkbox的树形控件使用说明 + * @data 应该是一个js数组 + * @id: 将树渲染到页面的某个div上,此div的id + * @checkId:需要默认勾选的数节点id;1.checkId="all",表示勾选所有节点 2.checkId=[1,2]表示勾选id为1,2的节点 + * 节点的id号由url传入json串中的id决定 + */ +function showCheckboxTree(data, id, checkId) { + var that = $("#" + id); + + menuTree = that.bind("loaded.jstree", function (e, data) { + that.jstree("open_all"); + that.find("li").each(function () { + if (checkId == 'all') { + that.jstree("check_node", $(this)); + } + + if (checkId instanceof Array) { + for (var i = 0; i < checkId.length; i++) { + if ($(this).attr("id") == checkId[i]) { + that.jstree("check_node", $(this)); + } + } + } + }); + }).jstree({ + "core": { + "data": data, + "attr": { + "class": "jstree-checked" + } + }, + "types": { + "default": { + "valid_children": ["default", "file"] + }, + "file": { + "icon": "glyphicon glyphicon-file", + "valid_children": [] + } + }, + "checkbox": { + "keep_selected_style": false, + "real_checkboxes": true + }, + "plugins": [ + "contextmenu", "dnd", "search", + "types", "checkbox" + ], + "contextmenu": { + "items": { + "create": null, + "rename": null, + "remove": null, + "ccp": null + } + } + }); + + // 加载完毕关闭所有节点 + that.bind('ready.jstree', function (obj, e) { + that.jstree('close_all'); + }); +} + +/** + * 获取所有选择的数据 + * + * @param treeId + */ +function getCheckTreeIds(treeId) { + // 打开所有的节点,不然获取不到子节点数据 + var that = $("#" + treeId); + that.jstree('open_all'); + + var ids = []; + var treeNode = that.jstree(true).get_selected(true); + + for (var i = 0; i < treeNode.length; i++) { + + var node = treeNode[i]; + var nodeId = node.original.id; + + // 判断是否重复 + if ($.inArray(nodeId, ids) == -1) { + ids.push(nodeId); + } + + for (var j = 0; j < node.parents.length; j++) { + // 判断是否重复 + var parentId = node.parents[j]; + if (parentId != "#" && $.inArray(parentId, ids) == -1) { + ids.push(parentId); + } + } + } + + that.jstree('close_all'); + return ids; +} \ No newline at end of file diff --git a/web/backend/resources/dist/js/ueditor.all.min.js b/web/backend/resources/dist/js/ueditor.all.min.js deleted file mode 100644 index d146ebd4c..000000000 --- a/web/backend/resources/dist/js/ueditor.all.min.js +++ /dev/null @@ -1,710 +0,0 @@ -(function(){function W(d,c,b){var a;c=c.toLowerCase();return(a=d.__allListeners||b&&(d.__allListeners={}))&&(a[c]||b&&(a[c]=[]))}function X(d,c,b,a,e,h){a=a&&d[c];var g;for(!a&&(a=d[b]);!a&&(g=(g||d).parentNode);){if("BODY"==g.tagName||h&&!h(g))return null;a=g[b]}return a&&e&&!e(a)?X(a,c,b,!1,e):a}UEDITOR_CONFIG=window.UEDITOR_CONFIG||{};var t=window.baidu||{};window.baidu=t;window.UE=t.editor=window.UE||{};UE.plugins={};UE.commands={};UE.instants={};UE.I18N={};UE._customizeUI={};UE.version="1.4.3"; -var M=UE.dom={},r=UE.browser=function(){var d=navigator.userAgent.toLowerCase(),c=window.opera,b={ie:/(msie\s|trident.*rv:)([\w.]+)/.test(d),opera:!!c&&c.version,webkit:-1a||b.quirks;b.ie9above=8a;b.ie11above=10a}b.gecko&&(e=d.match(/rv:([\d\.]+)/))&&(e=e[1].split("."),a=1E4*e[0]+100*(e[1]||0)+1*(e[2]||0));/chrome\/(\d+\.\d)/i.test(d)&&(b.chrome=+RegExp.$1);/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(d)&&!/chrome/i.test(d)&& -(b.safari=+(RegExp.$1||RegExp.$2));b.opera&&(a=parseFloat(c.version()));b.webkit&&(a=parseFloat(d.match(/ applewebkit\/(\d+)/)[1]));b.version=a;b.isCompatible=!b.mobile&&(b.ie&&6<=a||b.gecko&&10801<=a||b.opera&&9.5<=a||b.air&&1<=a||b.webkit&&522<=a||!1);return b}(),H=r.ie,la=r.opera,p=UE.utils={each:function(d,c,b){if(null!=d)if(d.length===+d.length)for(var a=0,e=d.length;a=b&&e===c)return a=h,!1});return a},removeItem:function(d,c){for(var b=0,a=d.length;b'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, -function(b,a){return a?b:{"<":"<","&":"&",'"':""",">":">","'":"'"}[b]}):""},unhtmlForUrl:function(d,c){return d?d.replace(c||/[<">']/g,function(b){return{"<":"<","&":"&",'"':""",">":">","'":"'"}[b]}):""},html:function(d){return d?d.replace(/&((g|l|quo)t|amp|#39|nbsp);/g,function(c){return{"<":"<","&":"&",""":'"',">":">","'":"'"," ":" "}[c]}):""},cssStyleToDomStyle:function(){var d=document.createElement("div").style,c={"float":void 0!= -d.cssFloat?"cssFloat":void 0!=d.styleFloat?"styleFloat":"float"};return function(b){return c[b]||(c[b]=b.toLowerCase().replace(/-./g,function(a){return a.charAt(1).toUpperCase()}))}}(),loadFile:function(){function d(b,a){try{for(var e=0,h;h=c[e++];)if(h.doc===b&&h.url==(a.src||a.href))return h}catch(g){return null}}var c=[];return function(b,a,e){var h=d(b,a);if(h)h.ready?e&&e():h.funs.push(e);else if(c.push({doc:b,url:a.src||a.href,funs:[e]}),!b.body){e=[];for(var g in a)"tag"!=g&&e.push(g+'="'+ -a[g]+'"');b.write("<"+a.tag+" "+e.join(" ")+" >")}else if(!a.id||!b.getElementById(a.id)){var l=b.createElement(a.tag);delete a.tag;for(g in a)l.setAttribute(g,a[g]);l.onload=l.onreadystatechange=function(){if(!this.readyState||/loaded|complete/.test(this.readyState)){h=d(b,a);if(0a?"0"+a:a},c=function(a){/["\\\x00-\x1f]/.test(a)&&(a=a.replace(/["\\\x00-\x1f]/g,function(a){var c=b[a];if(c)return c;c=a.charCodeAt();return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16)}));return'"'+a+'"'},b={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};return function(a){switch(typeof a){case "undefined":return"undefined";case "number":return isFinite(a)?String(a):"null";case "string":return c(a);case "boolean":return String(a); -default:if(null===a)return"null";if(p.isArray(a)){var e=["["],b=a.length,g,l,k;for(l=0;lr.version?{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan", -colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder"}:{tabindex:"tabIndex",readonly:"readOnly"},na=p.listToMap("-webkit-box -moz-box block list-item table table-row-group table-header-group table-footer-group table-row table-column-group table-column table-cell table-caption".split(" ")),f=M.domUtils={NODE_ELEMENT:1,NODE_DOCUMENT:9,NODE_TEXT:3,NODE_COMMENT:8,NODE_DOCUMENT_FRAGMENT:11,POSITION_IDENTICAL:0,POSITION_DISCONNECTED:1,POSITION_FOLLOWING:2,POSITION_PRECEDING:4,POSITION_IS_CONTAINED:8, -POSITION_CONTAINS:16,fillChar:H&&"6"==r.version?"\ufeff":"\u200b",keys:{8:1,46:1,16:1,17:1,18:1,37:1,38:1,39:1,40:1,13:1},getPosition:function(d,c){if(d===c)return 0;var b,a=[d],e=[c];for(b=d;b=b.parentNode;){if(b===c)return 10;a.push(b)}for(b=c;b=b.parentNode;){if(b===d)return 20;e.push(b)}a.reverse();e.reverse();if(a[0]!==e[0])return 1;for(b=-1;b++,a[b]===e[b];);d=a[b];for(c=e[b];d=d.nextSibling;)if(d===c)return 4;return 2},getNodeIndex:function(d,c){for(var b=d,a=0;b=b.previousSibling;)c&&3==b.nodeType? -b.nodeType!=b.nextSibling.nodeType&&a++:a++;return a},inDoc:function(d,c){return 10==f.getPosition(d,c)},findParent:function(d,c,b){if(d&&!f.isBody(d))for(d=b?d:d.parentNode;d;){if(!c||c(d)||f.isBody(d))return c&&!c(d)&&f.isBody(d)?null:d;d=d.parentNode}return null},findParentByTagName:function(d,c,b,a){c=p.listToMap(p.isArray(c)?c:[c]);return f.findParent(d,function(e){return c[e.tagName]&&!(a&&a(e))},b)},findParents:function(d,c,b,a){for(c=c&&(b&&b(d)||!b)?[d]:[];d=f.findParent(d,b);)c.push(d); -return a?c:c.reverse()},insertAfter:function(d,c){return d.nextSibling?d.parentNode.insertBefore(c,d.nextSibling):d.parentNode.appendChild(c)},remove:function(d,c){var b=d.parentNode,a;if(b){if(c&&d.hasChildNodes())for(;a=d.firstChild;)b.insertBefore(a,d);b.removeChild(d)}return d},getNextDomNode:function(d,c,b,a){return X(d,"firstChild","nextSibling",c,b,a)},getPreDomNode:function(d,c,b,a){return X(d,"lastChild","previousSibling",c,b,a)},isBookmarkNode:function(d){return 1==d.nodeType&&d.id&&/^_baidu_bookmark_/i.test(d.id)}, -getWindow:function(d){d=d.ownerDocument||d;return d.defaultView||d.parentWindow},getCommonAncestor:function(d,c){if(d===c)return d;for(var b=[d],a=[c],e=d,h=-1;e=e.parentNode;){if(e===c)return e;b.push(e)}for(e=c;e=e.parentNode;){if(e===d)return e;a.push(e)}b.reverse();for(a.reverse();h++,b[h]===a[h];);return 0==h?null:b[h-1]},clearEmptySibling:function(d,c,b){function a(a,c){for(var g;a&&!f.isBookmarkNode(a)&&(f.isEmptyInlineElement(a)||!(new RegExp("[^\t\n\r"+f.fillChar+"]")).test(a.nodeValue));)g= -a[c],f.remove(a),a=g}!c&&a(d.nextSibling,"nextSibling");!b&&a(d.previousSibling,"previousSibling")},split:function(d,c){var b=d.ownerDocument;if(r.ie&&c==d.nodeValue.length){var a=b.createTextNode("");return f.insertAfter(d,a)}a=d.splitText(c);r.ie8&&(b=b.createTextNode(""),f.insertAfter(a,b),f.remove(b));return a},isWhitespace:function(d){return!(new RegExp("[^ \t\n\r"+f.fillChar+"]")).test(d.nodeValue)},getXY:function(d){for(var c=0,b=0;d.offsetParent;)b+=d.offsetTop,c+=d.offsetLeft,d=d.offsetParent; -return{x:c,y:b}},on:function(d,c,b){var a=p.isArray(c)?c:p.trim(c).split(/\s+/),e=a.length;if(e)for(;e--;)if(c=a[e],d.addEventListener)d.addEventListener(c,b,!1);else{b._d||(b._d={els:[]});var h=c+b.toString(),g=p.indexOf(b._d.els,d);b._d[h]&&-1!=g||(-1==g&&b._d.els.push(d),b._d[h]||(b._d[h]=function(a){return b.call(a.srcElement,a||window.event)}),d.attachEvent("on"+c,b._d[h]))}d=null},un:function(d,c,b){var a=p.isArray(c)?c:p.trim(c).split(/\s+/),e=a.length;if(e)for(;e--;)if(c=a[e],d.removeEventListener)d.removeEventListener(c, -b,!1);else{var h=c+b.toString();try{d.detachEvent("on"+c,b._d?b._d[h]:b)}catch(g){}b._d&&b._d[h]&&(c=p.indexOf(b._d.els,d),-1!=c&&b._d.els.splice(c,1),0==b._d.els.length&&delete b._d[h])}},isSameElement:function(d,c){if(d.tagName!=c.tagName)return!1;var b=d.attributes,a=c.attributes;if(!H&&b.length!=a.length)return!1;for(var e,h,g=0,l=0,k=0;e=b[k++];){if("style"==e.nodeName)if(e.specified&&g++,f.isSameStyle(d,c))continue;else return!1;if(H)if(e.specified)g++,h=a.getNamedItem(e.nodeName);else continue; -else h=c.attributes[e.nodeName];if(!h.specified||e.nodeValue!=h.nodeValue)return!1}if(H){for(k=0;h=a[k++];)h.specified&&l++;if(g!=l)return!1}return!0},isSameStyle:function(d,c){var b=d.style.cssText.replace(/( ?; ?)/g,";").replace(/( ?: ?)/g,":"),a=c.style.cssText.replace(/( ?; ?)/g,";").replace(/( ?: ?)/g,":");if(r.opera){b=d.style;a=c.style;if(b.length!=a.length)return!1;for(var e in b)if(!/^(\d+|csstext)$/i.test(e)&&b[e]!=a[e])return!1;return!0}if(!b||!a)return b==a;b=b.split(";");a=a.split(";"); -if(b.length!=a.length)return!1;e=0;for(var h;h=b[e++];)if(-1==p.indexOf(a,h))return!1;return!0},isBlockElm:function(d){return 1==d.nodeType&&(x.$block[d.tagName]||na[f.getComputedStyle(d,"display")])&&!x.$nonChild[d.tagName]},isBody:function(d){return d&&1==d.nodeType&&"body"==d.tagName.toLowerCase()},breakParent:function(d,c){var b,a=d,e=d,h,g;do{a=a.parentNode;h?(b=a.cloneNode(!1),b.appendChild(h),h=b,b=a.cloneNode(!1),b.appendChild(g),g=b):(h=a.cloneNode(!1),g=h.cloneNode(!1));for(;b=e.previousSibling;)h.insertBefore(b, -h.firstChild);for(;b=e.nextSibling;)g.appendChild(b);e=a}while(c!==a);b=c.parentNode;b.insertBefore(h,c);b.insertBefore(g,c);b.insertBefore(d,g);f.remove(c);return d},isEmptyInlineElement:function(d){if(1!=d.nodeType||!x.$removeEmpty[d.tagName])return 0;for(d=d.firstChild;d;){if(f.isBookmarkNode(d)||1==d.nodeType&&!f.isEmptyInlineElement(d)||3==d.nodeType&&!f.isWhitespace(d))return 0;d=d.nextSibling}return 1},trimWhiteTextNode:function(d){function c(c){for(var a;(a=d[c])&&3==a.nodeType&&f.isWhitespace(a);)d.removeChild(a)} -c("firstChild");c("lastChild")},mergeChild:function(d,c,b){c=f.getElementsByTagName(d,d.tagName.toLowerCase());for(var a=0,e;e=c[a++];)if(e.parentNode&&!f.isBookmarkNode(e))if("span"==e.tagName.toLowerCase()){if(d===e.parentNode&&(f.trimWhiteTextNode(d),1==d.childNodes.length)){d.style.cssText=e.style.cssText+";"+d.style.cssText;f.remove(e,!0);continue}e.style.cssText=d.style.cssText+";"+e.style.cssText;if(b){var h=b.style;if(h)for(var h=h.split(";"),g=0,l;l=h[g++];)e.style[p.cssStyleToDomStyle(l.split(":")[0])]= -l.split(":")[1]}f.isSameStyle(e,d)&&f.remove(e,!0)}else f.isSameElement(d,e)&&f.remove(e,!0)},getElementsByTagName:function(d,c,b){if(b&&p.isString(b)){var a=b;b=function(g){return f.hasClass(g,a)}}c=p.trim(c).replace(/[ ]{2,}/g," ").split(" ");for(var e=[],h=0,g;g=c[h++];){g=d.getElementsByTagName(g);for(var l=0,k;k=g[l++];)b&&!b(k)||e.push(k)}return e},mergeToParent:function(d){for(var c=d.parentNode;c&&x.$removeEmpty[c.tagName];){if(c.tagName==d.tagName||"A"==c.tagName){f.trimWhiteTextNode(c); -if("SPAN"==c.tagName&&!f.isSameStyle(c,d)||"A"==c.tagName&&"SPAN"==d.tagName)if(1r.version&&"font-size"== -c&&!d.style.fontSize&&!x.$empty[d.tagName]&&!x.$nonChild[d.tagName]){var b=d.ownerDocument.createElement("span");b.style.cssText="padding:0;border:0;font-family:simsun;";b.innerHTML=".";d.appendChild(b);var a=b.offsetHeight;d.removeChild(b);b=null;return a+"px"}try{b=f.getStyle(d,c)||(window.getComputedStyle?f.getWindow(d).getComputedStyle(d,"").getPropertyValue(c):(d.currentStyle||d.style)[p.cssStyleToDomStyle(c)])}catch(e){return""}return p.transUnitToPx(p.fixColor(c,b))},removeClasses:function(d, -c){c=p.isArray(c)?c:p.trim(c).replace(/[ ]{2,}/g," ").split(" ");for(var b=0,a,e=d.className;a=c[b++];)e=e.replace(new RegExp("\\b"+a+"\\b"),"");(e=p.trim(e).replace(/[ ]{2,}/g," "))?d.className=e:f.removeAttributes(d,["class"])},addClass:function(d,c){if(d){c=p.trim(c).replace(/[ ]{2,}/g," ").split(" ");for(var b=0,a,e=d.className;a=c[b++];)(new RegExp("\\b"+a+"\\b")).test(e)||(e+=" "+a);d.className=p.trim(e)}},hasClass:function(d,c){if(p.isRegExp(c))return c.test(d.className);c=p.trim(c).replace(/[ ]{2,}/g, -" ").split(" ");for(var b=0,a,e=d.className;a=c[b++];)if(!(new RegExp("\\b"+a+"\\b","i")).test(e))return!1;return b-1==c.length},preventDefault:function(d){d.preventDefault?d.preventDefault():d.returnValue=!1},removeStyle:function(d,c){r.ie?("color"==c&&(c="(^|;)"+c),d.style.cssText=d.style.cssText.replace(new RegExp(c+"[^:]*:[^;]+;?","ig"),"")):d.style.removeProperty?d.style.removeProperty(c):d.style.removeAttribute(p.cssStyleToDomStyle(c));d.style.cssText||f.removeAttributes(d,["style"])},getStyle:function(d, -c){var b=d.style[p.cssStyleToDomStyle(c)];return p.fixColor(c,b)},setStyle:function(d,c,b){d.style[p.cssStyleToDomStyle(c)]=b;p.trim(d.style.cssText)||this.removeAttributes(d,"style")},setStyles:function(d,c){for(var b in c)c.hasOwnProperty(b)&&f.setStyle(d,b,c[b])},removeDirtyAttr:function(d){for(var c=0,b,a=d.getElementsByTagName("*");b=a[c++];)b.removeAttribute("_moz_dirty");d.removeAttribute("_moz_dirty")},getChildCount:function(d,c){var b=0,a=d.firstChild;for(c=c||function(){return 1};a;)c(a)&& -b++,a=a.nextSibling;return b},isEmptyNode:function(d){return!d.firstChild||0==f.getChildCount(d,function(c){return!f.isBr(c)&&!f.isBookmarkNode(c)&&!f.isWhitespace(c)})},clearSelectedArr:function(d){for(var c;c=d.pop();)f.removeAttributes(c,["class"])},scrollToView:function(d,c,b){var a=function(){var a=c.document,b="CSS1Compat"==a.compatMode;return{width:(b?a.documentElement.clientWidth:a.body.clientWidth)||0,height:(b?a.documentElement.clientHeight:a.body.clientHeight)||0}}().height;b=-1*a+b+(d.offsetHeight|| -0);d=f.getXY(d);b+=d.y;d=function(a){if("pageXOffset"in a)return{x:a.pageXOffset||0,y:a.pageYOffset||0};a=a.document;return{x:a.documentElement.scrollLeft||a.body.scrollLeft||0,y:a.documentElement.scrollTop||a.body.scrollTop||0}}(c).y;(b>d||bb?-20:20))},isBr:function(d){return 1==d.nodeType&&"BR"==d.tagName},isFillChar:function(d,c){if(3!=d.nodeType)return!1;var b=d.nodeValue;return c?(new RegExp("^"+f.fillChar)).test(b):!b.replace(new RegExp(f.fillChar,"g"),"").length},isStartInblock:function(d){d= -d.cloneRange();var c=0,b=d.startContainer,a;if(1==b.nodeType&&b.childNodes[d.startOffset])for(var b=b.childNodes[d.startOffset],e=b.previousSibling;e&&f.isFillChar(e);)b=e,e=e.previousSibling;this.isFillChar(b,!0)&&1==d.startOffset&&(d.setStartBefore(b),b=d.startContainer);for(;b&&f.isFillChar(b);)a=b,b=b.previousSibling;a&&(d.setStartBefore(a),b=d.startContainer);for(1==b.nodeType&&f.isEmptyNode(b)&&1==d.startOffset&&d.setStart(b,0).collapse(!0);!d.startOffset;){b=d.startContainer;if(f.isBlockElm(b)|| -f.isBody(b)){c=1;break}var e=d.startContainer.previousSibling,h;if(e){for(;e&&f.isFillChar(e);)h=e,e=e.previousSibling;h?d.setStartBefore(h):d.setStartBefore(d.startContainer)}else d.setStartBefore(d.startContainer)}return c&&!f.isBody(d.startContainer)?1:0},isEmptyBlock:function(d,c){if(1!=d.nodeType)return 0;c=c||new RegExp("[ \u00a0\t\r\n"+f.fillChar+"]","g");if(0/.test(d.outerHTML): -0==d.attributes.length},isCustomeNode:function(d){return 1==d.nodeType&&d.getAttribute("_ue_custom_node_")},isTagNode:function(d,c){return 1==d.nodeType&&(new RegExp("\\b"+d.tagName+"\\b","i")).test(c)},filterNodeList:function(d,c,b){var a=[];if(!p.isFunction(c)){var e=c;c=function(a){return-1!=p.indexOf(p.isArray(e)?e:e.split(" "),a.tagName.toLowerCase())}}p.each(d,function(b){c(b)&&a.push(b)});return 0==a.length?null:1!=a.length&&b?a:a[0]},isInNodeEndBoundary:function(d,c){var b=d.startContainer; -if(3==b.nodeType&&d.startOffset!=b.nodeValue.length||1==b.nodeType&&d.startOffset!=b.childNodes.length)return 0;for(;b!==c;){if(b.nextSibling)return 0;b=b.parentNode}return 1},isBoundaryNode:function(d,c){for(var b;!f.isBody(d);)if(b=d,d=d.parentNode,b!==d[c])return!1;return!0},fillHtml:r.ie11below?" ":"
    "},Q=new RegExp(f.fillChar,"g");(function(){function d(a){return!a.collapsed&&1==a.startContainer.nodeType&&a.startContainer===a.endContainer&&1==a.endOffset-a.startOffset}function c(a,g, -c,b){1==g.nodeType&&(x.$empty[g.tagName]||x.$nonChild[g.tagName])&&(c=f.getNodeIndex(g)+(a?0:1),g=g.parentNode);a?(b.startContainer=g,b.startOffset=c,b.endContainer||b.collapse(!0)):(b.endContainer=g,b.endOffset=c,b.startContainer||b.collapse(!1));b.collapsed=b.startContainer&&b.endContainer&&b.startContainer===b.endContainer&&b.startOffset==b.endOffset;return b}function b(a,g){var b=a.startContainer,c=a.endContainer,e=a.startOffset,l=a.endOffset,h=a.document,k=h.createDocumentFragment(),d,p;1==b.nodeType&& -(b=b.childNodes[e]||(d=b.appendChild(h.createTextNode(""))));1==c.nodeType&&(c=c.childNodes[l]||(p=c.appendChild(h.createTextNode(""))));if(b===c&&3==b.nodeType)return k.appendChild(h.createTextNode(b.substringData(e,l-e))),g&&(b.deleteData(e,l-e),a.collapse(!0)),k;for(var B,r,t=k,N=f.findParents(b,!0),x=f.findParents(c,!0),A=0;N[A]==x[A];)A++;for(var I=A,D;D=N[I];I++){B=D.nextSibling;D==b?d||(3==a.startContainer.nodeType?(t.appendChild(h.createTextNode(b.nodeValue.slice(e))),g&&b.deleteData(e,b.nodeValue.length- -e)):t.appendChild(g?b:b.cloneNode(!0))):(r=D.cloneNode(!1),t.appendChild(r));for(;B&&B!==c&&B!==x[I];)D=B.nextSibling,t.appendChild(g?B:B.cloneNode(!0)),B=D;t=r}t=k;N[A]||(t.appendChild(N[A-1].cloneNode(!1)),t=t.firstChild);for(I=A;e=x[I];I++){B=e.previousSibling;e==c?p||3!=a.endContainer.nodeType||(t.appendChild(h.createTextNode(c.substringData(0,l))),g&&c.deleteData(0,l)):(r=e.cloneNode(!1),t.appendChild(r));if(I!=A||!N[A])for(;B&&B!==b;)e=B.previousSibling,t.insertBefore(g?B:B.cloneNode(!0),t.firstChild), -B=e;t=r}g&&a.setStartBefore(x[A]?N[A]?x[A]:N[A-1]:x[A-1]).collapse(!0);d&&f.remove(d);p&&f.remove(p);return k}function a(a,g){try{if(l&&f.inDoc(l,a))if(l.nodeValue.replace(Q,"").length)l.nodeValue=l.nodeValue.replace(Q,"");else{var b=l.parentNode;for(f.remove(l);b&&f.isEmptyInlineElement(b)&&(r.safari?!(f.getPosition(b,g)&f.POSITION_CONTAINS):!b.contains(g));)l=b.parentNode,f.remove(b),b=l}}catch(c){}}function e(a,g){var b;for(a=a[g];a&&f.isFillChar(a);)b=a[g],f.remove(a),a=b}var h=0,g=f.fillChar, -l,k=M.Range=function(a){this.startContainer=this.startOffset=this.endContainer=this.endOffset=null;this.document=a;this.collapsed=!0};k.prototype={cloneContents:function(){return this.collapsed?null:b(this,0)},deleteContents:function(){var a;this.collapsed||b(this,1);r.webkit&&(a=this.startContainer,3!=a.nodeType||a.nodeValue.length||(this.setStartBefore(a).collapse(!0),f.remove(a)));return this},extractContents:function(){return this.collapsed?null:b(this,2)},setStart:function(a,g){return c(!0,a, -g,this)},setEnd:function(a,g){return c(!1,a,g,this)},setStartAfter:function(a){return this.setStart(a.parentNode,f.getNodeIndex(a)+1)},setStartBefore:function(a){return this.setStart(a.parentNode,f.getNodeIndex(a))},setEndAfter:function(a){return this.setEnd(a.parentNode,f.getNodeIndex(a)+1)},setEndBefore:function(a){return this.setEnd(a.parentNode,f.getNodeIndex(a))},setStartAtFirst:function(a){return this.setStart(a,0)},setStartAtLast:function(a){return this.setStart(a,3==a.nodeType?a.nodeValue.length: -a.childNodes.length)},setEndAtFirst:function(a){return this.setEnd(a,0)},setEndAtLast:function(a){return this.setEnd(a,3==a.nodeType?a.nodeValue.length:a.childNodes.length)},selectNode:function(a){return this.setStartBefore(a).setEndAfter(a)},selectNodeContents:function(a){return this.setStart(a,0).setEndAtLast(a)},cloneRange:function(){return(new k(this.document)).setStart(this.startContainer,this.startOffset).setEnd(this.endContainer,this.endOffset)},collapse:function(a){a?(this.endContainer=this.startContainer, -this.endOffset=this.startOffset):(this.startContainer=this.endContainer,this.startOffset=this.endOffset);this.collapsed=!0;return this},shrinkBoundary:function(a){function g(a){return 1==a.nodeType&&!f.isBookmarkNode(a)&&!x.$empty[a.tagName]&&!x.$nonChild[a.tagName]}for(var b,c=this.collapsed;1==this.startContainer.nodeType&&(b=this.startContainer.childNodes[this.startOffset])&&g(b);)this.setStart(b,0);if(c)return this.collapse(!0);if(!a)for(;1==this.endContainer.nodeType&&0=g.nodeValue.length)this.setStartAfter(g); -else{var l=f.split(g,b);g===e?this.setEnd(l,this.endOffset-b):g.parentNode===e&&(this.endOffset+=1);this.setStartBefore(l)}if(c)return this.collapse(!0)}a||(b=this.endOffset,e=this.endContainer,3==e.nodeType&&(0==b?this.setEndBefore(e):(b= -c.nodeValue.length)a["set"+b.replace(/(\w)/,function(a){return a.toUpperCase()})+"After"](c)}if(a||!this.collapsed)g(this,"start"),g(this,"end");return this},insertNode:function(a){var g=a,b=1;11==a.nodeType&&(g=a.firstChild,b=a.childNodes.length);this.trimBoundary(!0);var c=this.startContainer,e=c.childNodes[this.startOffset];e?c.insertBefore(a,e):c.appendChild(a);g.parentNode===this.endContainer&&(this.endOffset+=b);return this.setStartBefore(g)},setCursor:function(a,g){return this.collapse(!a).select(g)}, -createBookmark:function(a,g){var b,c=this.document.createElement("span");c.style.cssText="display:none;line-height:0px;";c.appendChild(this.document.createTextNode("\u200d"));c.id="_baidu_bookmark_start_"+(g?"":h++);this.collapsed||(b=c.cloneNode(!0),b.id="_baidu_bookmark_end_"+(g?"":h++));this.insertNode(c);b&&this.collapse().insertNode(b).setEndBefore(b);this.setStartAfter(c);return{start:a?c.id:c,end:b?a?b.id:b:null,id:a}},moveToBookmark:function(a){var g=a.id?this.document.getElementById(a.start): -a.start;a=a.end&&a.id?this.document.getElementById(a.end):a.end;this.setStartBefore(g);f.remove(g);a?(this.setEndBefore(a),f.remove(a)):this.collapse(!0);return this},enlarge:function(a,g){var b=f.isBody,c,e,l=this.document.createTextNode("");if(a){e=this.startContainer;1==e.nodeType?e.childNodes[this.startOffset]?c=e=e.childNodes[this.startOffset]:(e.appendChild(l),c=e=l):c=e;for(;;){if(f.isBlockElm(e)){for(e=c;(c=e.previousSibling)&&!f.isBlockElm(c);)e=c;this.setStartBefore(e);break}c=e;e=e.parentNode}e= -this.endContainer;1==e.nodeType?((c=e.childNodes[this.endOffset])?e.insertBefore(l,c):e.appendChild(l),c=e=l):c=e;for(;;){if(f.isBlockElm(e)){for(e=c;(c=e.nextSibling)&&!f.isBlockElm(c);)e=c;this.setEndAfter(e);break}c=e;e=e.parentNode}l.parentNode===this.endContainer&&this.endOffset--;f.remove(l)}if(!this.collapsed){for(;!(0!=this.startOffset||g&&g(this.startContainer)||b(this.startContainer));)this.setStartBefore(this.startContainer);for(;!(this.endOffset!=(1==this.endContainer.nodeType?this.endContainer.childNodes.length: -this.endContainer.nodeValue.length)||g&&g(this.endContainer)||b(this.endContainer));)this.setEndAfter(this.endContainer)}return this},enlargeToBlockElm:function(a){for(;!f.isBlockElm(this.startContainer);)this.setStartBefore(this.startContainer);if(!a)for(;!f.isBlockElm(this.endContainer);)this.setEndAfter(this.endContainer);return this},adjustmentBoundary:function(){if(!this.collapsed){for(;!f.isBody(this.startContainer)&&this.startOffset==this.startContainer[3==this.startContainer.nodeType?"nodeValue": -"childNodes"].length&&this.startContainer[3==this.startContainer.nodeType?"nodeValue":"childNodes"].length;)this.setStartAfter(this.startContainer);for(;!f.isBody(this.endContainer)&&!this.endOffset&&this.endContainer[3==this.endContainer.nodeType?"nodeValue":"childNodes"].length;)this.setEndBefore(this.endContainer)}return this},applyInlineStyle:function(a,g,b){if(this.collapsed)return this;this.trimBoundary().enlarge(!1,function(a){return 1==a.nodeType&&f.isBlockElm(a)}).adjustmentBoundary();for(var c= -this.createBookmark(),e=c.end,l=function(a){return 1==a.nodeType?"br"!=a.tagName.toLowerCase():!f.isWhitespace(a)},h=f.getNextDomNode(c.start,!1,l),k,d,p=this.cloneRange();h&&f.getPosition(h,e)&f.POSITION_PRECEDING;)if(3==h.nodeType||x[a][h.tagName]){p.setStartBefore(h);for(k=h;k&&(3==k.nodeType||x[a][k.tagName])&&k!==e;)d=k,k=f.getNextDomNode(k,1==k.nodeType,null,function(g){return x[a][g.tagName]});var h=p.setEndAfter(d).extractContents(),B;if(b&&0h&&(h=0);l.push(h);return l}var c={},e=this;c.startAddress=b(!0);a||(c.endAddress=e.collapsed?[].concat(c.startAddress):b());return c},moveToAddress:function(a,g){function b(a,g){for(var e=c.document.body,h,l,k=0,d,n=a.length;kw)n=u+1;else return{container:g,offset:c(k)}}if(-1==u){d.moveToElementText(g);d.setEndPoint("StartToStart",a);d=d.text.replace(/(\r\n|\r)/g,"\n").length;l=g.childNodes;if(!d)return k=l[l.length- -1],{container:k,offset:k.nodeValue.length};for(c=l.length;0r.version?"":"")+""+(b.iframeCssUrl?"":"")+(b.initialStyle?"": -"")+"