├── img
└── example.png
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── ISSUE_TEMPLATE.md
└── CONTRIBUTING.md
├── .eslintrc.json
├── package.json
├── LICENSE
├── .npmignore
├── .gitignore
├── README.ZH.md
├── README.md
├── index.js
└── lib
└── tagcanvas.js
/img/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/D0n9X1n/hexo-tag-cloud/HEAD/img/example.png
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Issue Fixed #
2 |
3 | ## Proposed Changes
4 |
5 | -
6 | -
7 | -
8 |
9 | @MikeCoder
10 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 6,
4 | "sourceType": "module",
5 | "ecmaFeatures": {
6 | "jsx": true
7 | }
8 | },
9 | "rules": {
10 | "semi": "error"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Expected Behavior
2 |
3 |
4 | ## Actual Behavior
5 |
6 |
7 | ## Steps to Reproduce the Problem
8 |
9 | 1.
10 | 1.
11 | 1.
12 |
13 | ## Specifications
14 | (The version of the project, operating system, hardware etc.)
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": {
3 | "name": "mike"
4 | },
5 | "bugs": {
6 | "url": "https://github.com/MikeCoder/hexo-tag-cloud/issues"
7 | },
8 | "dependencies": {
9 | "hexo-fs": "^0.2.2",
10 | "hexo-log": "^0.2.0"
11 | },
12 | "description": "Yet, just another tag cloud plugin for hexo",
13 | "directories": {},
14 | "keywords": [
15 | "tag",
16 | "cloud",
17 | "hexo"
18 | ],
19 | "license": "MIT",
20 | "main": "index.js",
21 | "maintainers": [
22 | {
23 | "email": "mike@mikecoder.cn",
24 | "name": "mikecoder"
25 | }
26 | ],
27 | "name": "hexo-tag-cloud",
28 | "optionalDependencies": {},
29 | "readmeFilename": "README.md",
30 | "repository": {
31 | "type": "git",
32 | "url": "git+https://github.com/MikeCoder/hexo-tag-cloud.git"
33 | },
34 | "scripts": {
35 | "test": "echo \"Error: no test specified\" && exit 1"
36 | },
37 | "version": "2.1.2"
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2016 TangDongxin
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the "Software"),
5 | to deal in the Software without restriction, including without limitation
6 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | and/or sell copies of the Software, and to permit persons to whom the
8 | Software is furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included
11 | in all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
18 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
21 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to Contribute to This Project
2 |
3 | #### **Did You Find a Bug?**
4 |
5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues].
6 | * If you're unable to find an open issue addressing the problem, [open a new one]. Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
7 |
8 | #### **Did You Write a Patch That Fixes a Bug?**
9 |
10 | * Open a new GitHub pull request with the patch.
11 | 1. Fork this project
12 | 1. Create your feature branch: `git checkout -b my-new-feature`
13 | 1. Commit your changes: `git commit -am 'Add some feature'`
14 | 1. Push to the branch: `git push origin my-new-feature`
15 | 1. Submit a pull request :tada:
16 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
17 |
18 | #### **Do You Intend to Add a New Feature or Change an Existing One?**
19 |
20 | * Suggest your change as a new [issue] using the label `enhancement` **BEFORE** you start writing code.
21 |
22 | Thanks for contributing! :heart:
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### *.iml template
3 | npm-debug.log
4 |
5 |
6 | ### .git template
7 |
8 |
9 | ### Tags template
10 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope
11 | TAGS
12 | !TAGS/
13 | tags
14 | !tags/
15 | gtags.files
16 | GTAGS
17 | GRTAGS
18 | GPATH
19 | cscope.files
20 | cscope.out
21 | cscope.in.out
22 | cscope.po.out
23 |
24 |
25 |
26 | ### Xcode template
27 | build/
28 | *.pbxuser
29 | !default.pbxuser
30 | *.mode1v3
31 | !default.mode1v3
32 | *.mode2v3
33 | !default.mode2v3
34 | *.perspectivev3
35 | !default.perspectivev3
36 | xcuserdata
37 | *.xccheckout
38 | *.moved-aside
39 | DerivedData
40 | *.xcuserstate
41 |
42 |
43 | ### Java template
44 | *.class
45 |
46 | # Mobile Tools for Java (J2ME)
47 | .mtj.tmp/
48 |
49 | # Package Files #
50 | *.jar
51 | *.war
52 | *.ear
53 |
54 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
55 | hs_err_pid*
56 |
57 |
58 | ### *.classpath template
59 |
60 |
61 | ### JetBrains template
62 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
63 |
64 | *.iml
65 |
66 | ## Directory-based project format:
67 | .idea/
68 | # if you remove the above rule, at least ignore the following:
69 |
70 | # User-specific stuff:
71 | .idea/workspace.xml
72 | .idea/tasks.xml
73 | .idea/dictionaries
74 |
75 | # Sensitive or high-churn files:
76 | .idea/dataSources.ids
77 | .idea/dataSources.xml
78 | .idea/sqlDataSources.xml
79 | .idea/dynamic.xml
80 | .idea/uiDesigner.xml
81 |
82 | # Gradle:
83 | # .idea/gradle.xml
84 | # .idea/libraries
85 |
86 | # Mongo Explorer plugin:
87 | # .idea/mongoSettings.xml
88 |
89 | ## File-based project format:
90 | *.ipr
91 | *.iws
92 |
93 | ## Plugin-specific files:
94 |
95 | # IntelliJ
96 | out/
97 |
98 | # mpeltonen/sbt-idea plugin
99 | .idea_modules/
100 |
101 | # JIRA plugin
102 | atlassian-ide-plugin.xml
103 |
104 | # Crashlytics plugin (for Android Studio and IntelliJ)
105 | com_crashlytics_export_strings.xml
106 | crashlytics.properties
107 | crashlytics-build.properties
108 |
109 |
110 | ### Vim template
111 | [._]*.s[a-w][a-z]
112 | [._]s[a-w][a-z]
113 | *.un~
114 | Session.vim
115 | .netrwhist
116 | *~
117 |
118 |
119 | ### *.settings template
120 | *.settings
121 | *.settings/
122 |
123 | ### Maven template
124 | target/
125 | pom.xml.tag
126 | pom.xml.releaseBackup
127 | pom.xml.versionsBackup
128 | pom.xml.next
129 | release.properties
130 | dependency-reduced-pom.xml
131 |
132 |
133 | ### Linux template
134 | *~
135 |
136 | # KDE directory preferences
137 | .directory
138 |
139 | # Linux trash folder which might appear on any partition or disk
140 | .Trash-*
141 |
142 |
143 | ### OSX template
144 | .DS_Store
145 | .AppleDouble
146 | .LSOverride
147 |
148 | # Icon must end with two \r
149 | Icon
150 |
151 | # Thumbnails
152 | ._*
153 |
154 | # Files that might appear in the root of a volume
155 | .DocumentRevisions-V100
156 | .fseventsd
157 | .Spotlight-V100
158 | .TemporaryItems
159 | .Trashes
160 | .VolumeIcon.icns
161 |
162 | # Directories potentially created on remote AFP share
163 | .AppleDB
164 | .AppleDesktop
165 | Network Trash Folder
166 | Temporary Items
167 | .apdisk
168 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### *.iml template
3 | npm-debug.log
4 | node_modules
5 | package-lock.json
6 |
7 |
8 | ### .git template
9 |
10 |
11 | ### Tags template
12 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope
13 | TAGS
14 | !TAGS/
15 | tags
16 | !tags/
17 | gtags.files
18 | GTAGS
19 | GRTAGS
20 | GPATH
21 | cscope.files
22 | cscope.out
23 | cscope.in.out
24 | cscope.po.out
25 |
26 |
27 |
28 | ### Xcode template
29 | build/
30 | *.pbxuser
31 | !default.pbxuser
32 | *.mode1v3
33 | !default.mode1v3
34 | *.mode2v3
35 | !default.mode2v3
36 | *.perspectivev3
37 | !default.perspectivev3
38 | xcuserdata
39 | *.xccheckout
40 | *.moved-aside
41 | DerivedData
42 | *.xcuserstate
43 |
44 |
45 | ### Java template
46 | *.class
47 |
48 | # Mobile Tools for Java (J2ME)
49 | .mtj.tmp/
50 |
51 | # Package Files #
52 | *.jar
53 | *.war
54 | *.ear
55 |
56 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
57 | hs_err_pid*
58 |
59 |
60 | ### *.classpath template
61 |
62 |
63 | ### JetBrains template
64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
65 |
66 | *.iml
67 |
68 | ## Directory-based project format:
69 | .idea/
70 | # if you remove the above rule, at least ignore the following:
71 |
72 | # User-specific stuff:
73 | .idea/workspace.xml
74 | .idea/tasks.xml
75 | .idea/dictionaries
76 |
77 | # Sensitive or high-churn files:
78 | .idea/dataSources.ids
79 | .idea/dataSources.xml
80 | .idea/sqlDataSources.xml
81 | .idea/dynamic.xml
82 | .idea/uiDesigner.xml
83 |
84 | # Gradle:
85 | # .idea/gradle.xml
86 | # .idea/libraries
87 |
88 | # Mongo Explorer plugin:
89 | # .idea/mongoSettings.xml
90 |
91 | ## File-based project format:
92 | *.ipr
93 | *.iws
94 |
95 | ## Plugin-specific files:
96 |
97 | # IntelliJ
98 | out/
99 |
100 | # mpeltonen/sbt-idea plugin
101 | .idea_modules/
102 |
103 | # JIRA plugin
104 | atlassian-ide-plugin.xml
105 |
106 | # Crashlytics plugin (for Android Studio and IntelliJ)
107 | com_crashlytics_export_strings.xml
108 | crashlytics.properties
109 | crashlytics-build.properties
110 |
111 |
112 | ### Vim template
113 | [._]*.s[a-w][a-z]
114 | [._]s[a-w][a-z]
115 | *.un~
116 | Session.vim
117 | .netrwhist
118 | *~
119 |
120 |
121 | ### *.settings template
122 | *.settings
123 | *.settings/
124 |
125 | ### Maven template
126 | target/
127 | pom.xml.tag
128 | pom.xml.releaseBackup
129 | pom.xml.versionsBackup
130 | pom.xml.next
131 | release.properties
132 | dependency-reduced-pom.xml
133 |
134 |
135 | ### Linux template
136 | *~
137 |
138 | # KDE directory preferences
139 | .directory
140 |
141 | # Linux trash folder which might appear on any partition or disk
142 | .Trash-*
143 |
144 |
145 | ### OSX template
146 | .DS_Store
147 | .AppleDouble
148 | .LSOverride
149 |
150 | # Icon must end with two \r
151 | Icon
152 |
153 | # Thumbnails
154 | ._*
155 |
156 | # Files that might appear in the root of a volume
157 | .DocumentRevisions-V100
158 | .fseventsd
159 | .Spotlight-V100
160 | .TemporaryItems
161 | .Trashes
162 | .VolumeIcon.icns
163 |
164 | # Directories potentially created on remote AFP share
165 | .AppleDB
166 | .AppleDesktop
167 | Network Trash Folder
168 | Temporary Items
169 | .apdisk
170 |
--------------------------------------------------------------------------------
/README.ZH.md:
--------------------------------------------------------------------------------
1 | # Hexo Tag Cloud
2 |
3 | 
4 | [](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/build-status/master)
5 | [](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/?branch=master)
6 |
7 | [English ReadMe](https://github.com/MikeCoder/hexo-tag-cloud/blob/master/README.md)
8 |
9 | Hexo 标签云插件
10 |
11 | ## 效果图
12 | 
13 |
14 | 这里是[效果预览站点](https://mhexo.github.io/archives/)
15 |
16 | ## 如何使用
17 | #### 安装
18 | + 进入到 hexo 的根目录,然后在 `package.json` 中添加依赖: `"hexo-tag-cloud": "2.1.*"`
19 | + 然后执行 `npm install` 命令
20 | + 然后需要你去修改主题的 tagcloud 的模板,这个依据你的主题而定。
21 |
22 | #### 对于 ejs 的用户
23 | + 这里以默认主题 landscape 为例。
24 | + tagcloud 模板文件为 `hexo/themes/landscape/layout/_widget/tagcloud.ejs`
25 | + 将这个文件修改为如下内容:
26 | ```
27 | <% if (site.tags.length) { %>
28 |
29 |
30 |
38 | <% } %>
39 | ```
40 | 如果你使用的是 [icarus](https://github.com/ppoffice/hexo-theme-icarus) 主题, 请查阅 [Issue #31](https://github.com/MikeCoder/hexo-tag-cloud/issues/31).
41 |
42 | #### 对于 swig 用户
43 | + 这里以 Next 主题为例。
44 | + 找到文件 `next/layout/_macro/sidebar.swig`, 然后添加如下内容。
45 | ```
46 | {% if site.tags.length > 1 %}
47 |
48 |
49 |
57 | {% endif %}
58 | ```
59 |
60 | #### 对于 jade 用户
61 | + 这里以 Apollo 主题为例
62 | + 找到 `apollo/layout/archive.jade` 文件,并且把 container 代码块修改为如下内容:
63 | ```
64 | block container
65 | include mixins/post
66 | .archive
67 | h2(class='archive-year')= 'Tag Cloud'
68 | script(type='text/javascript', charset='utf-8', src=url_for("/js/tagcloud.js"))
69 | script(type='text/javascript', charset='utf-8', src=url_for("/js/tagcanvas.js"))
70 |
71 | #myCanvasContainer.widget.tagcloud(align='center')
72 | canvas#resCanvas(width='500', height='500', style='width:100%')
73 | !=tagcloud()
74 | !=tagcloud()
75 | +postList()
76 | ```
77 |
78 | #### 对于 pug 用户
79 |
80 | + 这里以 Butterfly 主题为例
81 | + 找到 `Butterfly/layout/includes/widget/card_tags.pug` 文件
82 | + 将这个文件修改为如下内容(注意缩进):
83 |
84 | ```
85 | if site.tags.length
86 | .card-widget.card-tags
87 | .card-content
88 | .item-headline
89 | i.fa.fa-tags(aria-hidden="true")
90 | span= _p('aside.card_tags')
91 | script(type="text/javascript" charset="utf-8" src="/js/tagcloud.js")
92 | script(type="text/javascript" charset="utf-8" src="/js/tagcanvas.js")
93 | #myCanvasContainer.widget.tagcloud(align='center')
94 | canvas#resCanvas(width='200', height='200', style='width=100%')
95 | != tagcloud()
96 | != tagcloud({min_font: 16, max_font: 24, amount: 50, color: true, start_color: '#999', end_color: '#99a9bf'})
97 | ```
98 |
99 | #### 最后一步
100 |
101 | + 完成安装和显示,可以通过 `hexo clean && hexo g && hexo s` 来进行本地预览, hexo clean 为必须选项。
102 | + **PS:不要使用 `hexo g -d 或者 hexo d -g` 这类组合命令。**详情见: [Issue 7](https://github.com/MikeCoder/hexo-tag-cloud/issues/7)
103 |
104 | ## Troubleshooting
105 | 提交 issue 和截图以及 log
106 |
107 | ## 自定义
108 | 现在 hexo-tag-cloud 插件支持自定义啦。非常简单的步骤就可以改变你的标签云的字体和颜色,还有突出高亮。
109 |
110 | + 在你的博客根目录,找到 *_config.yml* 文件然后添加如下的配置项:
111 |
112 | ```
113 | # hexo-tag-cloud
114 | tag_cloud:
115 | textFont: Trebuchet MS, Helvetica
116 | textColor: '#333'
117 | textHeight: 25
118 | outlineColor: '#E2E1D1'
119 | maxSpeed: 0.5
120 | pauseOnSelected: false # true 意味着当选中对应 tag 时,停止转动
121 | ```
122 | + 然后使用 `hexo c && hexo g && hexo s` 来享受属于你自己的独一无二的标签云吧。
123 |
124 | ## 致谢
125 | + **[TagCanvas](http://www.goat1000.com/tagcanvas.php)**
126 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hexo-tag-cloud
2 |
3 | 
4 | [](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/build-status/master)
5 | [](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/?branch=master)
6 |
7 | [中文说明版本](https://github.com/MikeCoder/hexo-tag-cloud/blob/master/README.ZH.md)
8 |
9 | Yet, just another tag cloud plugin for hexo.
10 |
11 | ## How it looks like
12 | 
13 |
14 | And you can see online live demo by clicking [here](https://mhexo.github.io/archives/)
15 |
16 | ## How to Use
17 |
18 | #### Install
19 | + go into your hexo system folder, and add depandence `"hexo-tag-cloud": "2.1.*"` to `package.json`
20 | + then do *npm install* command
21 | + then you need to change your theme layout file and add the following content to that file depended on your render system.
22 |
23 | #### For ejs Users
24 | + For example, in its default theme landscape.
25 | + We should find `hexo/themes/landscape/layout/_widget/tagcloud.ejs` file and insert the following code.
26 | ```
27 | <% if (site.tags.length) { %>
28 |
29 |
30 |
38 | <% } %>
39 | ```
40 |
41 | If you are using [icarus](https://github.com/ppoffice/hexo-theme-icarus), please see [Issue #31](https://github.com/MikeCoder/hexo-tag-cloud/issues/31).
42 |
43 | #### For swig Users
44 | + Here we use theme Next as an example.
45 | + You should insert the following code into `next/layout/_macro/sidebar.swig`.
46 | ```
47 | {% if site.tags.length > 1 %}
48 |
49 |
50 |
58 | {% endif %}
59 | ```
60 | @See [Issue 6](https://github.com/MikeCoder/hexo-tag-cloud/issues/6)
61 |
62 |
63 | #### For jade Users
64 | + eg. theme Apollo.
65 | + You can add change the container block code to the following in `apollo/layout/archive.jade`.
66 | ```
67 | ...
68 | block container
69 | include mixins/post
70 | .archive
71 | h2(class='archive-year')= 'Tag Cloud'
72 | script(type='text/javascript', charset='utf-8', src=url_for("/js/tagcloud.js"))
73 | script(type='text/javascript', charset='utf-8', src=url_for("/js/tagcanvas.js"))
74 | #myCanvasContainer.widget.tagcloud(align='center')
75 | canvas#resCanvas(width='500', height='500', style='width:100%')
76 | !=tagcloud()
77 | !=tagcloud()
78 | +postList()
79 | ...
80 | ```
81 |
82 | #### For pug Users
83 |
84 | + Here we use theme Butterfly as an example.
85 | + Then find this file: `Butterfly/layout/includes/widget/card_tags.pug`
86 | + Modiefy the file as following code:
87 |
88 | ```
89 | if site.tags.length
90 | .card-widget.card-tags
91 | .card-content
92 | .item-headline
93 | i.fa.fa-tags(aria-hidden="true")
94 | span= _p('aside.card_tags')
95 | script(type="text/javascript" charset="utf-8" src="/js/tagcloud.js")
96 | script(type="text/javascript" charset="utf-8" src="/js/tagcanvas.js")
97 | #myCanvasContainer.widget.tagcloud(align='center')
98 | canvas#resCanvas(width='200', height='200', style='width=100%')
99 | != tagcloud()
100 | != tagcloud({min_font: 16, max_font: 24, amount: 50, color: true, start_color: '#999', end_color: '#99a9bf'})
101 | ```
102 |
103 |
104 | #### Last step
105 | + use `hexo clean && hexo g && hexo s` to see the change. hexo clean must be done before use `hexo g`.
106 | + **PS: Don't use the command `hexo g -d or hexo d -g`**, @See [Issue 7](https://github.com/MikeCoder/hexo-tag-cloud/issues/7)
107 |
108 | ## Customize
109 | Now the hexo-tag-cloud plugin support customize feature. It's simple to change the color and the font for the tag cloud.
110 |
111 | + Add these config below to your *_config.yml* file(which under your blog root directory)
112 |
113 | ```
114 | # hexo-tag-cloud
115 | tag_cloud:
116 | textFont: 'Trebuchet MS, Helvetica'
117 | textColor: '#333'
118 | textHeight: 25
119 | outlineColor: '#E2E1D1'
120 | maxSpeed: 0.5 # range from [0.01 ~ 1]
121 | pauseOnSelected: false # true means pause the cloud tag movement when highlight a tag
122 | ```
123 | + then use `hexo clean && hexo g && hexo s` to enjoy your different tag cloud
124 |
125 | ## Troubleshooting
126 | Submit issue please
127 |
128 | # Thanks
129 | + **[TagCanvas](http://www.goat1000.com/tagcanvas.php)**
130 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Copyright © 2016 TangDongxin
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining
4 | // a copy of this software and associated documentation files (the "Software"),
5 | // to deal in the Software without restriction, including without limitation
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and/or sell copies of the Software, and to permit persons to whom the
8 | // Software is furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included
11 | // in all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
18 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
21 | "use strict";
22 |
23 | var fs = require("hexo-fs");
24 | var pathFn = require("path");
25 | var Hexo = require("hexo");
26 | var log = require("hexo-log")({
27 | debug: false,
28 | silent: false
29 | });
30 |
31 | hexo.extend.filter.register("after_generate", function(post) {
32 | var libPath = pathFn.join(
33 | pathFn.join(pathFn.join(hexo.base_dir, "node_modules"), "hexo-tag-cloud"),
34 | "lib"
35 | );
36 |
37 | var tagcanvasPubPath = pathFn.join(
38 | pathFn.join(hexo.public_dir, "js"),
39 | "tagcanvas.js"
40 | );
41 | var tagcloudPubPath = pathFn.join(
42 | pathFn.join(hexo.public_dir, "js"),
43 | "tagcloud.js"
44 | );
45 |
46 | log.info("---- START COPYING TAG CLOUD FILES ----");
47 | fs.copyFile(pathFn.join(libPath, "tagcanvas.js"), tagcanvasPubPath);
48 |
49 | var textFont = "Helvetica";
50 | var textColor = "#333";
51 | var textHeight = "15";
52 | var outlineColor = "#E2E1C1";
53 | var maxSpeed = "0.03";
54 | var pauseOnSelected = true;
55 |
56 | if (hexo.config.tag_cloud) {
57 | if (hexo.config.tag_cloud.textColor) {
58 | textColor = hexo.config.tag_cloud.textColor;
59 | }
60 | if (hexo.config.tag_cloud.textFont) {
61 | textFont = hexo.config.tag_cloud.textFont;
62 | }
63 | if (hexo.config.tag_cloud.textHeight) {
64 | textHeight = hexo.config.tag_cloud.textHeight;
65 | }
66 | if (hexo.config.tag_cloud.outlineColor) {
67 | outlineColor = hexo.config.tag_cloud.outlineColor;
68 | }
69 | if (hexo.config.tag_cloud.maxSpeed) {
70 | maxSpeed = hexo.config.tag_cloud.maxSpeed;
71 | }
72 | if (hexo.config.tag_cloud.pauseOnSelected != undefined) {
73 | pauseOnSelected = hexo.config.tag_cloud.pauseOnSelected;
74 | }
75 | }
76 |
77 | var tagCloudJsContent =
78 | " function addLoadEvent(func) {\n" +
79 | " var oldonload = window.onload;\n" +
80 | " if (typeof window.onload != 'function') {\n" +
81 | " window.onload = func;\n" +
82 | " } else {\n" +
83 | " window.onload = function() {\n" +
84 | " oldonload();\n" +
85 | " func();\n" +
86 | " }\n" +
87 | " }\n" +
88 | " }\n" +
89 | "\n" +
90 | " addLoadEvent(function() {\n" +
91 | " console.log('tag cloud plugin rock and roll!');\n" +
92 | "\n" +
93 | " try {\n" +
94 | " TagCanvas.textFont = '${textFont}';\n" +
95 | " TagCanvas.textColour = '${textColor}';\n" +
96 | " TagCanvas.textHeight = ${textHeight};\n" +
97 | " TagCanvas.outlineColour = '${outlineColor}';\n" +
98 | " TagCanvas.maxSpeed = ${maxSpeed};\n" +
99 | " TagCanvas.freezeActive = ${pauseOnSelected};\n" +
100 | " TagCanvas.outlineMethod = 'block';\n" +
101 | " TagCanvas.minBrightness = 0.2;\n" +
102 | " TagCanvas.depth = 0.92;\n" +
103 | " TagCanvas.pulsateTo = 0.6;\n" +
104 | " TagCanvas.initial = [0.1,-0.1];\n" +
105 | " TagCanvas.decel = 0.98;\n" +
106 | " TagCanvas.reverse = true;\n" +
107 | " TagCanvas.hideTags = false;\n" +
108 | " TagCanvas.shadow = '#ccf';\n" +
109 | " TagCanvas.shadowBlur = 3;\n" +
110 | " TagCanvas.weight = false;\n" +
111 | " TagCanvas.imageScale = null;\n" +
112 | " TagCanvas.fadeIn = 1000;\n" +
113 | " TagCanvas.clickToFront = 600;\n" +
114 | " TagCanvas.lock = false;\n" +
115 | " TagCanvas.Start('resCanvas');\n" +
116 | " TagCanvas.tc['resCanvas'].Wheel(true)\n" +
117 | " } catch(e) {\n" +
118 | " console.log(e);\n" +
119 | " document.getElementById('myCanvasContainer').style.display = 'none';\n" +
120 | " }\n" +
121 | " });\n";
122 |
123 | tagCloudJsContent = tagCloudJsContent.replace("${textFont}", textFont);
124 | tagCloudJsContent = tagCloudJsContent.replace("${textColor}", textColor);
125 | tagCloudJsContent = tagCloudJsContent.replace("${textHeight}", textHeight);
126 | tagCloudJsContent = tagCloudJsContent.replace("${outlineColor}", outlineColor);
127 | tagCloudJsContent = tagCloudJsContent.replace("${maxSpeed}", maxSpeed);
128 | tagCloudJsContent = tagCloudJsContent.replace("${pauseOnSelected}", pauseOnSelected);
129 |
130 | fs.writeFile(tagcloudPubPath, tagCloudJsContent);
131 | log.info("---- END COPYING TAG CLOUD FILES ----");
132 | });
133 |
--------------------------------------------------------------------------------
/lib/tagcanvas.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2010-2015 Graham Breach
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with this program. If not, see .
16 | */
17 | /**
18 | * TagCanvas 2.9
19 | * For more information, please contact
20 | */
21 | (function(){
22 | "use strict";
23 | var i, j, abs = Math.abs, sin = Math.sin, cos = Math.cos, max = Math.max,
24 | min = Math.min, ceil = Math.ceil, sqrt = Math.sqrt, pow = Math.pow,
25 | hexlookup3 = {}, hexlookup2 = {}, hexlookup1 = {
26 | 0:"0,", 1:"17,", 2:"34,", 3:"51,", 4:"68,", 5:"85,",
27 | 6:"102,", 7:"119,", 8:"136,", 9:"153,", a:"170,", A:"170,",
28 | b:"187,", B:"187,", c:"204,", C:"204,", d:"221,", D:"221,",
29 | e:"238,", E:"238,", f:"255,", F:"255,"
30 | }, Oproto, Tproto, TCproto, Mproto, Vproto, TSproto, TCVproto,
31 | doc = document, ocanvas, handlers = {};
32 | for(i = 0; i < 256; ++i) {
33 | j = i.toString(16);
34 | if(i < 16)
35 | j = '0' + j;
36 | hexlookup2[j] = hexlookup2[j.toUpperCase()] = i.toString() + ',';
37 | }
38 | function Defined(d) {
39 | return typeof d != 'undefined';
40 | }
41 | function IsObject(o) {
42 | return typeof o == 'object' && o != null;
43 | }
44 | function Clamp(v, mn, mx) {
45 | return isNaN(v) ? mx : min(mx, max(mn, v));
46 | }
47 | function Nop() {
48 | return false;
49 | }
50 | function TimeNow() {
51 | return new Date().valueOf();
52 | }
53 | function SortList(l, f) {
54 | var nl = [], tl = l.length, i;
55 | for(i = 0; i < tl; ++i)
56 | nl.push(l[i]);
57 | nl.sort(f);
58 | return nl;
59 | }
60 | function Shuffle(a) {
61 | var i = a.length-1, t, p;
62 | while(i) {
63 | p = ~~(Math.random()*i);
64 | t = a[i];
65 | a[i] = a[p];
66 | a[p] = t;
67 | --i;
68 | }
69 | }
70 | function Vector(x, y, z) {
71 | this.x = x;
72 | this.y = y;
73 | this.z = z;
74 | }
75 | Vproto = Vector.prototype;
76 | Vproto.length = function() {
77 | return sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
78 | };
79 | Vproto.dot = function(v) {
80 | return this.x * v.x + this.y * v.y + this.z * v.z;
81 | };
82 | Vproto.cross = function(v) {
83 | var x = this.y * v.z - this.z * v.y,
84 | y = this.z * v.x - this.x * v.z,
85 | z = this.x * v.y - this.y * v.x;
86 | return new Vector(x, y, z);
87 | };
88 | Vproto.angle = function(v) {
89 | var dot = this.dot(v), ac;
90 | if(dot == 0)
91 | return Math.PI / 2.0;
92 | ac = dot / (this.length() * v.length());
93 | if(ac >= 1)
94 | return 0;
95 | if(ac <= -1)
96 | return Math.PI;
97 | return Math.acos(ac);
98 | };
99 | Vproto.unit = function() {
100 | var l = this.length();
101 | return new Vector(this.x / l, this.y / l, this.z / l);
102 | };
103 | function MakeVector(lg, lt) {
104 | lt = lt * Math.PI / 180;
105 | lg = lg * Math.PI / 180;
106 | var x = sin(lg) * cos(lt), y = -sin(lt), z = -cos(lg) * cos(lt);
107 | return new Vector(x, y, z);
108 | }
109 | function Matrix(a) {
110 | this[1] = {1: a[0], 2: a[1], 3: a[2]};
111 | this[2] = {1: a[3], 2: a[4], 3: a[5]};
112 | this[3] = {1: a[6], 2: a[7], 3: a[8]};
113 | }
114 | Mproto = Matrix.prototype;
115 | Matrix.Identity = function() {
116 | return new Matrix([1,0,0, 0,1,0, 0,0,1]);
117 | };
118 | Matrix.Rotation = function(angle, u) {
119 | var sina = sin(angle), cosa = cos(angle), mcos = 1 - cosa;
120 | return new Matrix([
121 | cosa + pow(u.x, 2) * mcos, u.x * u.y * mcos - u.z * sina, u.x * u.z * mcos + u.y * sina,
122 | u.y * u.x * mcos + u.z * sina, cosa + pow(u.y, 2) * mcos, u.y * u.z * mcos - u.x * sina,
123 | u.z * u.x * mcos - u.y * sina, u.z * u.y * mcos + u.x * sina, cosa + pow(u.z, 2) * mcos
124 | ]);
125 | }
126 | Mproto.mul = function(m) {
127 | var a = [], i, j, mmatrix = (m.xform ? 1 : 0);
128 | for(i = 1; i <= 3; ++i)
129 | for(j = 1; j <= 3; ++j) {
130 | if(mmatrix)
131 | a.push(this[i][1] * m[1][j] +
132 | this[i][2] * m[2][j] +
133 | this[i][3] * m[3][j]);
134 | else
135 | a.push(this[i][j] * m);
136 | }
137 | return new Matrix(a);
138 | };
139 | Mproto.xform = function(p) {
140 | var a = {}, x = p.x, y = p.y, z = p.z;
141 | a.x = x * this[1][1] + y * this[2][1] + z * this[3][1];
142 | a.y = x * this[1][2] + y * this[2][2] + z * this[3][2];
143 | a.z = x * this[1][3] + y * this[2][3] + z * this[3][3];
144 | return a;
145 | };
146 | function PointsOnSphere(n,xr,yr,zr,magic) {
147 | var i, y, r, phi, pts = [], off = 2/n, inc;
148 | inc = Math.PI * (3 - sqrt(5) + (parseFloat(magic) ? parseFloat(magic) : 0));
149 | for(i = 0; i < n; ++i) {
150 | y = i * off - 1 + (off / 2);
151 | r = sqrt(1 - y*y);
152 | phi = i * inc;
153 | pts.push([cos(phi) * r * xr, y * yr, sin(phi) * r * zr]);
154 | }
155 | return pts;
156 | }
157 | function Cylinder(n,o,xr,yr,zr,magic) {
158 | var phi, pts = [], off = 2/n, inc, i, j, k, l;
159 | inc = Math.PI * (3 - sqrt(5) + (parseFloat(magic) ? parseFloat(magic) : 0));
160 | for(i = 0; i < n; ++i) {
161 | j = i * off - 1 + (off / 2);
162 | phi = i * inc;
163 | k = cos(phi);
164 | l = sin(phi);
165 | pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
166 | }
167 | return pts;
168 | }
169 | function Ring(o, n, xr, yr, zr, j) {
170 | var phi, pts = [], inc = Math.PI * 2 / n, i, k, l;
171 | for(i = 0; i < n; ++i) {
172 | phi = i * inc;
173 | k = cos(phi);
174 | l = sin(phi);
175 | pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
176 | }
177 | return pts;
178 | }
179 | function PointsOnCylinderV(n,xr,yr,zr,m) { return Cylinder(n, 0, xr, yr, zr, m) }
180 | function PointsOnCylinderH(n,xr,yr,zr,m) { return Cylinder(n, 1, xr, yr, zr, m) }
181 | function PointsOnRingV(n, xr, yr, zr, offset) {
182 | offset = isNaN(offset) ? 0 : offset * 1;
183 | return Ring(0, n, xr, yr, zr, offset);
184 | }
185 | function PointsOnRingH(n, xr, yr, zr, offset) {
186 | offset = isNaN(offset) ? 0 : offset * 1;
187 | return Ring(1, n, xr, yr, zr, offset);
188 | }
189 | function CentreImage(t) {
190 | var i = new Image;
191 | i.onload = function() {
192 | var dx = i.width / 2, dy = i.height / 2;
193 | t.centreFunc = function(c, w, h, cx, cy) {
194 | c.setTransform(1, 0, 0, 1, 0, 0);
195 | c.globalAlpha = 1;
196 | c.drawImage(i, cx - dx, cy - dy);
197 | };
198 | };
199 | i.src = t.centreImage;
200 | }
201 | function SetAlpha(c,a) {
202 | var d = c, p1, p2, ae = (a*1).toPrecision(3) + ')';
203 | if(c[0] === '#') {
204 | if(!hexlookup3[c])
205 | if(c.length === 4)
206 | hexlookup3[c] = 'rgba(' + hexlookup1[c[1]] + hexlookup1[c[2]] + hexlookup1[c[3]];
207 | else
208 | hexlookup3[c] = 'rgba(' + hexlookup2[c.substr(1,2)] + hexlookup2[c.substr(3,2)] + hexlookup2[c.substr(5,2)];
209 | d = hexlookup3[c] + ae;
210 | } else if(c.substr(0,4) === 'rgb(' || c.substr(0,4) === 'hsl(') {
211 | d = (c.replace('(','a(').replace(')', ',' + ae));
212 | } else if(c.substr(0,5) === 'rgba(' || c.substr(0,5) === 'hsla(') {
213 | p1 = c.lastIndexOf(',') + 1, p2 = c.indexOf(')');
214 | a *= parseFloat(c.substring(p1,p2));
215 | d = c.substr(0,p1) + a.toPrecision(3) + ')';
216 | }
217 | return d;
218 | }
219 | function NewCanvas(w,h) {
220 | // if using excanvas, give up now
221 | if(window.G_vmlCanvasManager)
222 | return null;
223 | var c = doc.createElement('canvas');
224 | c.width = w;
225 | c.height = h;
226 | return c;
227 | }
228 | // I think all browsers pass this test now...
229 | function ShadowAlphaBroken() {
230 | var cv = NewCanvas(3,3), c, i;
231 | if(!cv)
232 | return false;
233 | c = cv.getContext('2d');
234 | c.strokeStyle = '#000';
235 | c.shadowColor = '#fff';
236 | c.shadowBlur = 3;
237 | c.globalAlpha = 0;
238 | c.strokeRect(2,2,2,2);
239 | c.globalAlpha = 1;
240 | i = c.getImageData(2,2,1,1);
241 | cv = null;
242 | return (i.data[0] > 0);
243 | }
244 | function SetGradient(c, l, o, g) {
245 | var gd = c.createLinearGradient(0, 0, l, 0), i;
246 | for(i in g)
247 | gd.addColorStop(1 - i, g[i]);
248 | c.fillStyle = gd;
249 | c.fillRect(0, o, l, 1);
250 | }
251 | function FindGradientColour(tc, p, r) {
252 | var l = 1024, h = 1, gl = tc.weightGradient, cv, c, i, d;
253 | if(tc.gCanvas) {
254 | c = tc.gCanvas.getContext('2d');
255 | h = tc.gCanvas.height;
256 | } else {
257 | if(IsObject(gl[0]))
258 | h = gl.length;
259 | else
260 | gl = [gl];
261 | tc.gCanvas = cv = NewCanvas(l, h);
262 | if(!cv)
263 | return null;
264 | c = cv.getContext('2d');
265 | for(i = 0; i < h; ++i)
266 | SetGradient(c, l, i, gl[i]);
267 | }
268 | r = max(min(r || 0, h - 1), 0);
269 | d = c.getImageData(~~((l - 1) * p), r, 1, 1).data;
270 | return 'rgba(' + d[0] + ',' + d[1] + ',' + d[2] + ',' + (d[3]/255) + ')';
271 | }
272 | function TextSet(ctxt, font, colour, strings, padx, pady, shadowColour,
273 | shadowBlur, shadowOffsets, maxWidth, widths, align) {
274 | var xo = padx + (shadowBlur || 0) +
275 | (shadowOffsets.length && shadowOffsets[0] < 0 ? abs(shadowOffsets[0]) : 0),
276 | yo = pady + (shadowBlur || 0) +
277 | (shadowOffsets.length && shadowOffsets[1] < 0 ? abs(shadowOffsets[1]) : 0), i, xc;
278 | ctxt.font = font;
279 | ctxt.textBaseline = 'top';
280 | ctxt.fillStyle = colour;
281 | shadowColour && (ctxt.shadowColor = shadowColour);
282 | shadowBlur && (ctxt.shadowBlur = shadowBlur);
283 | shadowOffsets.length && (ctxt.shadowOffsetX = shadowOffsets[0],
284 | ctxt.shadowOffsetY = shadowOffsets[1]);
285 | for(i = 0; i < strings.length; ++i) {
286 | xc = 0;
287 | if(widths) {
288 | if('right' == align) {
289 | xc = maxWidth - widths[i];
290 | } else if('centre' == align) {
291 | xc = (maxWidth - widths[i]) / 2;
292 | }
293 | }
294 | ctxt.fillText(strings[i], xo + xc, yo);
295 | yo += parseInt(font);
296 | }
297 | }
298 | function RRect(c, x, y, w, h, r, s) {
299 | if(r) {
300 | c.beginPath();
301 | c.moveTo(x, y + h - r);
302 | c.arcTo(x, y, x + r, y, r);
303 | c.arcTo(x + w, y, x + w, y + r, r);
304 | c.arcTo(x + w, y + h, x + w - r, y + h, r);
305 | c.arcTo(x, y + h, x, y + h - r, r);
306 | c.closePath();
307 | c[s ? 'stroke' : 'fill']();
308 | } else {
309 | c[s ? 'strokeRect' : 'fillRect'](x, y, w, h);
310 | }
311 | }
312 | function TextCanvas(strings, font, w, h, maxWidth, stringWidths, align, valign,
313 | scale) {
314 | this.strings = strings;
315 | this.font = font;
316 | this.width = w;
317 | this.height = h;
318 | this.maxWidth = maxWidth;
319 | this.stringWidths = stringWidths;
320 | this.align = align;
321 | this.valign = valign;
322 | this.scale = scale;
323 | }
324 | TCVproto = TextCanvas.prototype;
325 | TCVproto.SetImage = function(image, w, h, position, padding, align, valign,
326 | scale) {
327 | this.image = image;
328 | this.iwidth = w * this.scale;
329 | this.iheight = h * this.scale;
330 | this.ipos = position;
331 | this.ipad = padding * this.scale;
332 | this.iscale = scale;
333 | this.ialign = align;
334 | this.ivalign = valign;
335 | };
336 | TCVproto.Align = function(size, space, a) {
337 | var pos = 0;
338 | if(a == 'right' || a == 'bottom')
339 | pos = space - size;
340 | else if(a != 'left' && a != 'top')
341 | pos = (space - size) / 2;
342 | return pos;
343 | };
344 | TCVproto.Create = function(colour, bgColour, bgOutline, bgOutlineThickness,
345 | shadowColour, shadowBlur, shadowOffsets, padding, radius) {
346 | var cv, cw, ch, c, x1, x2, y1, y2, offx, offy, ix, iy, iw, ih, rr,
347 | sox = abs(shadowOffsets[0]), soy = abs(shadowOffsets[1]), shadowcv, shadowc;
348 | padding = max(padding, sox + shadowBlur, soy + shadowBlur);
349 | x1 = 2 * (padding + bgOutlineThickness);
350 | y1 = 2 * (padding + bgOutlineThickness);
351 | cw = this.width + x1;
352 | ch = this.height + y1;
353 | offx = offy = padding + bgOutlineThickness;
354 |
355 | if(this.image) {
356 | ix = iy = padding + bgOutlineThickness;
357 | iw = this.iwidth;
358 | ih = this.iheight;
359 | if(this.ipos == 'top' || this.ipos == 'bottom') {
360 | if(iw < this.width)
361 | ix += this.Align(iw, this.width, this.ialign);
362 | else
363 | offx += this.Align(this.width, iw, this.align);
364 | if(this.ipos == 'top')
365 | offy += ih + this.ipad;
366 | else
367 | iy += this.height + this.ipad;
368 | cw = max(cw, iw + x1);
369 | ch += ih + this.ipad;
370 | } else {
371 | if(ih < this.height)
372 | iy += this.Align(ih, this.height, this.ivalign);
373 | else
374 | offy += this.Align(this.height, ih, this.valign);
375 | if(this.ipos == 'right')
376 | ix += this.width + this.ipad;
377 | else
378 | offx += iw + this.ipad;
379 | cw += iw + this.ipad;
380 | ch = max(ch, ih + y1);
381 | }
382 | }
383 |
384 | cv = NewCanvas(cw, ch);
385 | if(!cv)
386 | return null;
387 | x1 = y1 = bgOutlineThickness / 2;
388 | x2 = cw - bgOutlineThickness;
389 | y2 = ch - bgOutlineThickness;
390 | rr = min(radius, x2 / 2, y2 / 2);
391 | c = cv.getContext('2d');
392 | if(bgColour) {
393 | c.fillStyle = bgColour;
394 | RRect(c, x1, y1, x2, y2, rr);
395 | }
396 | if(bgOutlineThickness) {
397 | c.strokeStyle = bgOutline;
398 | c.lineWidth = bgOutlineThickness;
399 | RRect(c, x1, y1, x2, y2, rr, true);
400 | }
401 | if(shadowBlur || sox || soy) {
402 | // use a transparent canvas to draw on
403 | shadowcv = NewCanvas(cw, ch);
404 | if(shadowcv) {
405 | shadowc = c;
406 | c = shadowcv.getContext('2d');
407 | }
408 | }
409 |
410 | // don't use TextSet shadow support because it adds space for shadow
411 | TextSet(c, this.font, colour, this.strings, offx, offy, 0, 0, [],
412 | this.maxWidth, this.stringWidths, this.align);
413 |
414 | if(this.image)
415 | c.drawImage(this.image, ix, iy, iw, ih);
416 |
417 | if(shadowc) {
418 | // draw the text and image with the added shadow
419 | c = shadowc;
420 | shadowColour && (c.shadowColor = shadowColour);
421 | shadowBlur && (c.shadowBlur = shadowBlur);
422 | c.shadowOffsetX = shadowOffsets[0];
423 | c.shadowOffsetY = shadowOffsets[1];
424 | c.drawImage(shadowcv, 0, 0);
425 | }
426 | return cv;
427 | };
428 | function ExpandImage(i, w, h) {
429 | var cv = NewCanvas(w, h), c;
430 | if(!cv)
431 | return null;
432 | c = cv.getContext('2d');
433 | c.drawImage(i, (w - i.width) / 2, (h - i.height) / 2);
434 | return cv;
435 | }
436 | function ScaleImage(i, w, h) {
437 | var cv = NewCanvas(w, h), c;
438 | if(!cv)
439 | return null;
440 | c = cv.getContext('2d');
441 | c.drawImage(i, 0, 0, w, h);
442 | return cv;
443 | }
444 | function AddBackgroundToImage(i, w, h, scale, colour, othickness, ocolour,
445 | padding, radius, ofill) {
446 | var cw = w + ((2 * padding) + othickness) * scale,
447 | ch = h + ((2 * padding) + othickness) * scale,
448 | cv = NewCanvas(cw, ch), c, x1, y1, x2, y2, ocanvas, cc, rr;
449 | if(!cv)
450 | return null;
451 | othickness *= scale;
452 | radius *= scale;
453 | x1 = y1 = othickness / 2;
454 | x2 = cw - othickness;
455 | y2 = ch - othickness;
456 | padding = (padding * scale) + x1; // add space for outline
457 | c = cv.getContext('2d');
458 | rr = min(radius, x2 / 2, y2 / 2);
459 | if(colour) {
460 | c.fillStyle = colour;
461 | RRect(c, x1, y1, x2, y2, rr);
462 | }
463 | if(othickness) {
464 | c.strokeStyle = ocolour;
465 | c.lineWidth = othickness;
466 | RRect(c, x1, y1, x2, y2, rr, true);
467 | }
468 |
469 | if(ofill) {
470 | // use compositing to colour in the image and border
471 | ocanvas = NewCanvas(cw, ch);
472 | cc = ocanvas.getContext('2d');
473 | cc.drawImage(i, padding, padding, w, h);
474 | cc.globalCompositeOperation = 'source-in';
475 | cc.fillStyle = ocolour;
476 | cc.fillRect(0, 0, cw, ch);
477 | cc.globalCompositeOperation = 'destination-over';
478 | cc.drawImage(cv, 0, 0);
479 | cc.globalCompositeOperation = 'source-over';
480 | c.drawImage(ocanvas, 0, 0);
481 | } else {
482 | c.drawImage(i, padding, padding, i.width, i.height);
483 | }
484 | return {image: cv, width: cw / scale, height: ch / scale};
485 | }
486 | /**
487 | * Rounds off the corners of an image
488 | */
489 | function RoundImage(i, r, iw, ih, s) {
490 | var cv, c, r1 = parseFloat(r), l = max(iw, ih);
491 | cv = NewCanvas(iw, ih);
492 | if(!cv)
493 | return null;
494 | if(r.indexOf('%') > 0)
495 | r1 = l * r1 / 100;
496 | else
497 | r1 = r1 * s;
498 | c = cv.getContext('2d');
499 | c.globalCompositeOperation = 'source-over';
500 | c.fillStyle = '#fff';
501 | if(r1 >= l/2) {
502 | r1 = min(iw,ih) / 2;
503 | c.beginPath();
504 | c.moveTo(iw/2,ih/2);
505 | c.arc(iw/2,ih/2,r1,0,2*Math.PI,false);
506 | c.fill();
507 | c.closePath();
508 | } else {
509 | r1 = min(iw/2,ih/2,r1);
510 | RRect(c, 0, 0, iw, ih, r1, true);
511 | c.fill();
512 | }
513 | c.globalCompositeOperation = 'source-in';
514 | c.drawImage(i, 0, 0, iw, ih);
515 | return cv;
516 | }
517 | /**
518 | * Creates a new canvas containing the image and its shadow
519 | * Returns an object containing the image and its dimensions at z=0
520 | */
521 | function AddShadowToImage(i, w, h, scale, sc, sb, so) {
522 | var sw = abs(so[0]), sh = abs(so[1]),
523 | cw = w + (sw > sb ? sw + sb : sb * 2) * scale,
524 | ch = h + (sh > sb ? sh + sb : sb * 2) * scale,
525 | xo = scale * ((sb || 0) + (so[0] < 0 ? sw : 0)),
526 | yo = scale * ((sb || 0) + (so[1] < 0 ? sh : 0)), cv, c;
527 | cv = NewCanvas(cw, ch);
528 | if(!cv)
529 | return null;
530 | c = cv.getContext('2d');
531 | sc && (c.shadowColor = sc);
532 | sb && (c.shadowBlur = sb * scale);
533 | so && (c.shadowOffsetX = so[0] * scale, c.shadowOffsetY = so[1] * scale);
534 | c.drawImage(i, xo, yo, w, h);
535 | return {image: cv, width: cw / scale, height: ch / scale};
536 | }
537 | function FindTextBoundingBox(s,f,ht) {
538 | var w = parseInt(s.toString().length * ht), h = parseInt(ht * 2 * s.length),
539 | cv = NewCanvas(w,h), c, idata, w1, h1, x, y, i, ex;
540 | if(!cv)
541 | return null;
542 | c = cv.getContext('2d');
543 | c.fillStyle = '#000';
544 | c.fillRect(0,0,w,h);
545 | TextSet(c,ht + 'px ' + f,'#fff',s,0,0,0,0,[],'centre')
546 |
547 | idata = c.getImageData(0,0,w,h);
548 | w1 = idata.width; h1 = idata.height;
549 | ex = {
550 | min: { x: w1, y: h1 },
551 | max: { x: -1, y: -1 }
552 | };
553 | for(y = 0; y < h1; ++y) {
554 | for(x = 0; x < w1; ++x) {
555 | i = (y * w1 + x) * 4;
556 | if(idata.data[i+1] > 0) {
557 | if(x < ex.min.x) ex.min.x = x;
558 | if(x > ex.max.x) ex.max.x = x;
559 | if(y < ex.min.y) ex.min.y = y;
560 | if(y > ex.max.y) ex.max.y = y;
561 | }
562 | }
563 | }
564 | // device pixels might not be css pixels
565 | if(w1 != w) {
566 | ex.min.x *= (w / w1);
567 | ex.max.x *= (w / w1);
568 | }
569 | if(h1 != h) {
570 | ex.min.y *= (w / h1);
571 | ex.max.y *= (w / h1);
572 | }
573 |
574 | cv = null;
575 | return ex;
576 | }
577 | function FixFont(f) {
578 | return "'" + f.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'";
579 | }
580 | function AddHandler(h,f,e) {
581 | e = e || doc;
582 | if(e.addEventListener)
583 | e.addEventListener(h,f,false);
584 | else
585 | e.attachEvent('on' + h, f);
586 | }
587 | function RemoveHandler(h,f,e) {
588 | e = e || doc;
589 | if(e.removeEventListener)
590 | e.removeEventListener(h, f);
591 | else
592 | e.detachEvent('on' + h, f);
593 | }
594 | function AddImage(i, o, t, tc) {
595 | var s = tc.imageScale, mscale, ic, bc, oc, iw, ih;
596 | // image not loaded, wait for image onload
597 | if(!o.complete)
598 | return AddHandler('load',function() { AddImage(i,o,t,tc); }, o);
599 | if(!i.complete)
600 | return AddHandler('load',function() { AddImage(i,o,t,tc); }, i);
601 |
602 | // Yes, this does look like nonsense, but it makes sure that both the
603 | // width and height are actually set and not just calculated. This is
604 | // required to keep proportional sizes when the images are hidden, so
605 | // the images can be used again for another cloud.
606 | o.width = o.width;
607 | o.height = o.height;
608 |
609 | if(s) {
610 | i.width = o.width * s;
611 | i.height = o.height * s;
612 | }
613 | // the standard width of the image, with imageScale applied
614 | t.iw = i.width;
615 | t.ih = i.height;
616 | if(tc.txtOpt) {
617 | ic = i;
618 | mscale = tc.zoomMax * tc.txtScale;
619 | iw = t.iw * mscale;
620 | ih = t.ih * mscale;
621 | if(iw < o.naturalWidth || ih < o.naturalHeight) {
622 | ic = ScaleImage(i, iw, ih);
623 | if(ic)
624 | t.fimage = ic;
625 | } else {
626 | iw = t.iw;
627 | ih = t.ih;
628 | mscale = 1;
629 | }
630 | if(parseFloat(tc.imageRadius))
631 | t.image = t.fimage = i = RoundImage(t.image, tc.imageRadius, iw, ih, mscale);
632 | if(!t.HasText()) {
633 | if(tc.shadow) {
634 | ic = AddShadowToImage(t.image, iw, ih, mscale, tc.shadow, tc.shadowBlur,
635 | tc.shadowOffset);
636 | if(ic) {
637 | t.fimage = ic.image;
638 | t.w = ic.width;
639 | t.h = ic.height;
640 | }
641 | }
642 | if(tc.bgColour || tc.bgOutlineThickness) {
643 | bc = tc.bgColour == 'tag' ? GetProperty(t.a, 'background-color') :
644 | tc.bgColour;
645 | oc = tc.bgOutline == 'tag' ? GetProperty(t.a, 'color') :
646 | (tc.bgOutline || tc.textColour);
647 | iw = t.fimage.width;
648 | ih = t.fimage.height;
649 | if(tc.outlineMethod == 'colour') {
650 | // create the outline version first, using the current image state
651 | ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
652 | tc.bgOutlineThickness, t.outline.colour, tc.padding, tc.bgRadius, 1);
653 | if(ic)
654 | t.oimage = ic.image;
655 | }
656 | ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
657 | tc.bgOutlineThickness, oc, tc.padding, tc.bgRadius);
658 | if(ic) {
659 | t.fimage = ic.image;
660 | t.w = ic.width;
661 | t.h = ic.height;
662 | }
663 | }
664 | if(tc.outlineMethod == 'size') {
665 | if(tc.outlineIncrease > 0) {
666 | t.iw += 2 * tc.outlineIncrease;
667 | t.ih += 2 * tc.outlineIncrease;
668 | iw = mscale * t.iw;
669 | ih = mscale * t.ih;
670 | ic = ScaleImage(t.fimage, iw, ih);
671 | t.oimage = ic;
672 | t.fimage = ExpandImage(t.fimage, t.oimage.width, t.oimage.height);
673 | } else {
674 | iw = mscale * (t.iw + (2 * tc.outlineIncrease));
675 | ih = mscale * (t.ih + (2 * tc.outlineIncrease));
676 | ic = ScaleImage(t.fimage, iw, ih);
677 | t.oimage = ExpandImage(ic, t.fimage.width, t.fimage.height);
678 | }
679 | }
680 | }
681 | }
682 | t.Init();
683 | }
684 | function GetProperty(e,p) {
685 | var dv = doc.defaultView, pc = p.replace(/\-([a-z])/g,function(a){return a.charAt(1).toUpperCase()});
686 | return (dv && dv.getComputedStyle && dv.getComputedStyle(e,null).getPropertyValue(p)) ||
687 | (e.currentStyle && e.currentStyle[pc]);
688 | }
689 | function FindWeight(a, wFrom, tHeight) {
690 | var w = 1, p;
691 | if(wFrom) {
692 | w = 1 * (a.getAttribute(wFrom) || tHeight);
693 | } else if(p = GetProperty(a,'font-size')) {
694 | w = (p.indexOf('px') > -1 && p.replace('px','') * 1) ||
695 | (p.indexOf('pt') > -1 && p.replace('pt','') * 1.25) ||
696 | p * 3.3;
697 | }
698 | return w;
699 | }
700 | function EventToCanvasId(e) {
701 | return e.target && Defined(e.target.id) ? e.target.id :
702 | e.srcElement.parentNode.id;
703 | }
704 | function EventXY(e, c) {
705 | var xy, p, xmul = parseInt(GetProperty(c, 'width')) / c.width,
706 | ymul = parseInt(GetProperty(c, 'height')) / c.height;
707 | if(Defined(e.offsetX)) {
708 | xy = {x: e.offsetX, y: e.offsetY};
709 | } else {
710 | p = AbsPos(c.id);
711 | if(Defined(e.changedTouches))
712 | e = e.changedTouches[0];
713 | if(e.pageX)
714 | xy = {x: e.pageX - p.x, y: e.pageY - p.y};
715 | }
716 | if(xy && xmul && ymul) {
717 | xy.x /= xmul;
718 | xy.y /= ymul;
719 | }
720 | return xy;
721 | }
722 | function MouseOut(e) {
723 | var cv = e.target || e.fromElement.parentNode, tc = TagCanvas.tc[cv.id];
724 | if(tc) {
725 | tc.mx = tc.my = -1;
726 | tc.UnFreeze();
727 | tc.EndDrag();
728 | }
729 | }
730 | function MouseMove(e) {
731 | var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
732 | for(i in t.tc) {
733 | tc = t.tc[i];
734 | if(tc.tttimer) {
735 | clearTimeout(tc.tttimer);
736 | tc.tttimer = null;
737 | }
738 | }
739 | if(tg && t.tc[tg]) {
740 | tc = t.tc[tg];
741 | if(p = EventXY(e, tc.canvas)) {
742 | tc.mx = p.x;
743 | tc.my = p.y;
744 | tc.Drag(e, p);
745 | }
746 | tc.drawn = 0;
747 | }
748 | }
749 | function MouseDown(e) {
750 | var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
751 | tg = EventToCanvasId(e);
752 | if(tg && e.button == cb && t.tc[tg]) {
753 | t.tc[tg].BeginDrag(e);
754 | }
755 | }
756 | function MouseUp(e) {
757 | var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
758 | tg = EventToCanvasId(e), tc;
759 | if(tg && e.button == cb && t.tc[tg]) {
760 | tc = t.tc[tg];
761 | MouseMove(e);
762 | if(!tc.EndDrag() && !tc.touchState)
763 | tc.Clicked(e);
764 | }
765 | }
766 | function TouchDown(e) {
767 | var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]), p;
768 | if(tc && e.changedTouches) {
769 | if(e.touches.length == 1 && tc.touchState == 0) {
770 | tc.touchState = 1;
771 | tc.BeginDrag(e);
772 | if(p = EventXY(e, tc.canvas)) {
773 | tc.mx = p.x;
774 | tc.my = p.y;
775 | tc.drawn = 0;
776 | }
777 | } else if(e.targetTouches.length == 2 && tc.pinchZoom) {
778 | tc.touchState = 3;
779 | tc.EndDrag();
780 | tc.BeginPinch(e);
781 | } else {
782 | tc.EndDrag();
783 | tc.EndPinch();
784 | tc.touchState = 0;
785 | }
786 | }
787 | }
788 | function TouchUp(e) {
789 | var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]);
790 | if(tc && e.changedTouches) {
791 | switch(tc.touchState) {
792 | case 1:
793 | tc.Draw();
794 | tc.Clicked();
795 | break;
796 | case 2:
797 | tc.EndDrag();
798 | break;
799 | case 3:
800 | tc.EndPinch();
801 | }
802 | tc.touchState = 0;
803 | }
804 | }
805 | function TouchMove(e) {
806 | var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
807 | for(i in t.tc) {
808 | tc = t.tc[i];
809 | if(tc.tttimer) {
810 | clearTimeout(tc.tttimer);
811 | tc.tttimer = null;
812 | }
813 | }
814 | tc = (tg && t.tc[tg]);
815 | if(tc && e.changedTouches && tc.touchState) {
816 | switch(tc.touchState) {
817 | case 1:
818 | case 2:
819 | if(p = EventXY(e, tc.canvas)) {
820 | tc.mx = p.x;
821 | tc.my = p.y;
822 | if(tc.Drag(e, p))
823 | tc.touchState = 2;
824 | }
825 | break;
826 | case 3:
827 | tc.Pinch(e);
828 | }
829 | tc.drawn = 0;
830 | }
831 | }
832 | function MouseWheel(e) {
833 | var t = TagCanvas, tg = EventToCanvasId(e);
834 | if(tg && t.tc[tg]) {
835 | e.cancelBubble = true;
836 | e.returnValue = false;
837 | e.preventDefault && e.preventDefault();
838 | t.tc[tg].Wheel((e.wheelDelta || e.detail) > 0);
839 | }
840 | }
841 | function Scroll(e) {
842 | var i, t = TagCanvas;
843 | clearTimeout(t.scrollTimer);
844 | for(i in t.tc) {
845 | t.tc[i].Pause();
846 | }
847 | t.scrollTimer = setTimeout(function() {
848 | var i, t = TagCanvas;
849 | for(i in t.tc) {
850 | t.tc[i].Resume();
851 | }
852 | }, t.scrollPause);
853 | }
854 | function DrawCanvas() {
855 | DrawCanvasRAF(TimeNow());
856 | }
857 | function DrawCanvasRAF(t) {
858 | var tc = TagCanvas.tc, i;
859 | TagCanvas.NextFrame(TagCanvas.interval);
860 | t = t || TimeNow();
861 | for(i in tc)
862 | tc[i].Draw(t);
863 | }
864 | function AbsPos(id) {
865 | var e = doc.getElementById(id), r = e.getBoundingClientRect(),
866 | dd = doc.documentElement, b = doc.body, w = window,
867 | xs = w.pageXOffset || dd.scrollLeft,
868 | ys = w.pageYOffset || dd.scrollTop,
869 | xo = dd.clientLeft || b.clientLeft,
870 | yo = dd.clientTop || b.clientTop;
871 | return { x: r.left + xs - xo, y: r.top + ys - yo };
872 | }
873 | function Project(tc,p1,sx,sy) {
874 | var m = tc.radius * tc.z1 / (tc.z1 + tc.z2 + p1.z);
875 | return {
876 | x: p1.x * m * sx,
877 | y: p1.y * m * sy,
878 | z: p1.z,
879 | w: (tc.z1 - p1.z) / tc.z2
880 | };
881 | }
882 | /**
883 | * @constructor
884 | * for recursively splitting tag contents on
tags
885 | */
886 | function TextSplitter(e) {
887 | this.e = e;
888 | this.br = 0;
889 | this.line = [];
890 | this.text = [];
891 | this.original = e.innerText || e.textContent;
892 | }
893 | TSproto = TextSplitter.prototype;
894 | TSproto.Empty = function() {
895 | for(var i = 0; i < this.text.length; ++i)
896 | if(this.text[i].length)
897 | return false;
898 | return true;
899 | };
900 | TSproto.Lines = function(e) {
901 | var r = e ? 1 : 0, cn, cl, i;
902 | e = e || this.e;
903 | cn = e.childNodes;
904 | cl = cn.length;
905 |
906 | for(i = 0; i < cl; ++i) {
907 | if(cn[i].nodeName == 'BR') {
908 | this.text.push(this.line.join(' '));
909 | this.br = 1;
910 | } else if(cn[i].nodeType == 3) {
911 | if(this.br) {
912 | this.line = [cn[i].nodeValue];
913 | this.br = 0;
914 | } else {
915 | this.line.push(cn[i].nodeValue);
916 | }
917 | } else {
918 | this.Lines(cn[i]);
919 | }
920 | }
921 | r || this.br || this.text.push(this.line.join(' '));
922 | return this.text;
923 | };
924 | TSproto.SplitWidth = function(w, c, f, h) {
925 | var i, j, words, text = [];
926 | c.font = h + 'px ' + f;
927 | for(i = 0; i < this.text.length; ++i) {
928 | words = this.text[i].split(/\s+/);
929 | this.line = [words[0]];
930 | for(j = 1; j < words.length; ++j) {
931 | if(c.measureText(this.line.join(' ') + ' ' + words[j]).width > w) {
932 | text.push(this.line.join(' '));
933 | this.line = [words[j]];
934 | } else {
935 | this.line.push(words[j]);
936 | }
937 | }
938 | text.push(this.line.join(' '));
939 | }
940 | return this.text = text;
941 | };
942 | /**
943 | * @constructor
944 | */
945 | function Outline(tc,t) {
946 | this.ts = null;
947 | this.tc = tc;
948 | this.tag = t;
949 | this.x = this.y = this.w = this.h = this.sc = 1;
950 | this.z = 0;
951 | this.pulse = 1;
952 | this.pulsate = tc.pulsateTo < 1;
953 | this.colour = tc.outlineColour;
954 | this.adash = ~~tc.outlineDash;
955 | this.agap = ~~tc.outlineDashSpace || this.adash;
956 | this.aspeed = tc.outlineDashSpeed * 1;
957 | if(this.colour == 'tag')
958 | this.colour = GetProperty(t.a, 'color');
959 | else if(this.colour == 'tagbg')
960 | this.colour = GetProperty(t.a, 'background-color');
961 | this.Draw = this.pulsate ? this.DrawPulsate : this.DrawSimple;
962 | this.radius = tc.outlineRadius | 0;
963 | this.SetMethod(tc.outlineMethod);
964 | }
965 | Oproto = Outline.prototype;
966 | Oproto.SetMethod = function(om) {
967 | var methods = {
968 | block: ['PreDraw','DrawBlock'],
969 | colour: ['PreDraw','DrawColour'],
970 | outline: ['PostDraw','DrawOutline'],
971 | classic: ['LastDraw','DrawOutline'],
972 | size: ['PreDraw','DrawSize'],
973 | none: ['LastDraw']
974 | }, funcs = methods[om] || methods.outline;
975 | if(om == 'none') {
976 | this.Draw = function() { return 1; }
977 | } else {
978 | this.drawFunc = this[funcs[1]];
979 | }
980 | this[funcs[0]] = this.Draw;
981 | };
982 | Oproto.Update = function(x,y,w,h,sc,z,xo,yo) {
983 | var o = this.tc.outlineOffset, o2 = 2 * o;
984 | this.x = sc * x + xo - o;
985 | this.y = sc * y + yo - o;
986 | this.w = sc * w + o2;
987 | this.h = sc * h + o2;
988 | this.sc = sc; // used to determine frontmost
989 | this.z = z;
990 | };
991 | Oproto.Ants = function(c) {
992 | if(!this.adash)
993 | return;
994 | var l = this.adash, g = this.agap, s = this.aspeed, length = l + g,
995 | l1 = 0, l2 = l, g1 = g, g2 = 0, seq = 0, ants;
996 | if(s) {
997 | seq = abs(s) * (TimeNow() - this.ts) / 50;
998 | if(s < 0)
999 | seq = 8.64e6 - seq;
1000 | s = ~~seq % length;
1001 | }
1002 | if(s) {
1003 | if(l >= s) {
1004 | l1 = l - s;
1005 | l2 = s;
1006 | } else {
1007 | g1 = length - s;
1008 | g2 = g - g1;
1009 | }
1010 | ants = [l1, g1, l2, g2];
1011 | } else {
1012 | ants = [l,g];
1013 | }
1014 | c.setLineDash(ants);
1015 | }
1016 | Oproto.DrawOutline = function(c,x,y,w,h,colour) {
1017 | var r = min(this.radius, h/2, w/2);
1018 | c.strokeStyle = colour;
1019 | this.Ants(c);
1020 | RRect(c, x, y, w, h, r, true);
1021 | };
1022 | Oproto.DrawSize = function(c,x,y,w,h,colour,tag,x1,y1) {
1023 | var tw = tag.w, th = tag.h, m, i, sc;
1024 | if(this.pulsate) {
1025 | if(tag.image)
1026 | sc = (tag.image.height + this.tc.outlineIncrease) / tag.image.height;
1027 | else
1028 | sc = tag.oscale;
1029 | i = tag.fimage || tag.image;
1030 | m = 1 + ((sc - 1) * (1-this.pulse));
1031 | tag.h *= m;
1032 | tag.w *= m;
1033 | } else {
1034 | i = tag.oimage;
1035 | }
1036 | tag.alpha = 1;
1037 | tag.Draw(c, x1, y1, i);
1038 | tag.h = th;
1039 | tag.w = tw;
1040 | return 1;
1041 | };
1042 | Oproto.DrawColour = function(c,x,y,w,h,colour,tag,x1,y1) {
1043 | if(tag.oimage) {
1044 | if(this.pulse < 1) {
1045 | tag.alpha = 1 - pow(this.pulse, 2);
1046 | tag.Draw(c, x1, y1, tag.fimage);
1047 | tag.alpha = this.pulse;
1048 | } else {
1049 | tag.alpha = 1;
1050 | }
1051 | tag.Draw(c, x1, y1, tag.oimage);
1052 | return 1;
1053 | }
1054 | return this[tag.image ? 'DrawColourImage' : 'DrawColourText'](c,x,y,w,h,colour,tag,x1,y1);
1055 | };
1056 | Oproto.DrawColourText = function(c,x,y,w,h,colour,tag,x1,y1) {
1057 | var normal = tag.colour;
1058 | tag.colour = colour;
1059 | tag.alpha = 1;
1060 | tag.Draw(c,x1,y1);
1061 | tag.colour = normal;
1062 | return 1;
1063 | };
1064 | Oproto.DrawColourImage = function(c,x,y,w,h,colour,tag,x1,y1) {
1065 | var ccanvas = c.canvas, fx = ~~max(x,0), fy = ~~max(y,0),
1066 | fw = min(ccanvas.width - fx, w) + .5|0, fh = min(ccanvas.height - fy,h) + .5|0, cc;
1067 | if(ocanvas)
1068 | ocanvas.width = fw, ocanvas.height = fh;
1069 | else
1070 | ocanvas = NewCanvas(fw, fh);
1071 | if(!ocanvas)
1072 | return this.SetMethod('outline'); // if using IE and images, give up!
1073 | cc = ocanvas.getContext('2d');
1074 |
1075 | cc.drawImage(ccanvas,fx,fy,fw,fh,0,0,fw,fh);
1076 | c.clearRect(fx,fy,fw,fh);
1077 | if(this.pulsate) {
1078 | tag.alpha = 1 - pow(this.pulse, 2);
1079 | } else {
1080 | tag.alpha = 1;
1081 | }
1082 | tag.Draw(c,x1,y1);
1083 | c.setTransform(1,0,0,1,0,0);
1084 | c.save();
1085 | c.beginPath();
1086 | c.rect(fx,fy,fw,fh);
1087 | c.clip();
1088 | c.globalCompositeOperation = 'source-in';
1089 | c.fillStyle = colour;
1090 | c.fillRect(fx,fy,fw,fh);
1091 | c.restore();
1092 | c.globalAlpha = 1;
1093 | c.globalCompositeOperation = 'destination-over';
1094 | c.drawImage(ocanvas,0,0,fw,fh,fx,fy,fw,fh);
1095 | c.globalCompositeOperation = 'source-over';
1096 | return 1;
1097 | };
1098 | Oproto.DrawBlock = function(c,x,y,w,h,colour) {
1099 | var r = min(this.radius, h/2, w/2);
1100 | c.fillStyle = colour;
1101 | RRect(c, x, y, w, h, r);
1102 | };
1103 | Oproto.DrawSimple = function(c, tag, x1, y1, ga, useGa) {
1104 | var t = this.tc;
1105 | c.setTransform(1,0,0,1,0,0);
1106 | c.strokeStyle = this.colour;
1107 | c.lineWidth = t.outlineThickness;
1108 | c.shadowBlur = c.shadowOffsetX = c.shadowOffsetY = 0;
1109 | c.globalAlpha = useGa ? ga : 1;
1110 | return this.drawFunc(c,this.x,this.y,this.w,this.h,this.colour,tag,x1,y1);
1111 | };
1112 | Oproto.DrawPulsate = function(c, tag, x1, y1) {
1113 | var diff = TimeNow() - this.ts, t = this.tc,
1114 | ga = t.pulsateTo + ((1 - t.pulsateTo) *
1115 | (0.5 + (cos(2 * Math.PI * diff / (1000 * t.pulsateTime)) / 2)));
1116 | this.pulse = ga = TagCanvas.Smooth(1,ga);
1117 | return this.DrawSimple(c, tag, x1, y1, ga, 1);
1118 | };
1119 | Oproto.Active = function(c,x,y) {
1120 | var a = (x >= this.x && y >= this.y &&
1121 | x <= this.x + this.w && y <= this.y + this.h);
1122 | if(a) {
1123 | this.ts = this.ts || TimeNow();
1124 | } else {
1125 | this.ts = null;
1126 | }
1127 | return a;
1128 | };
1129 | Oproto.PreDraw = Oproto.PostDraw = Oproto.LastDraw = Nop;
1130 | /**
1131 | * @constructor
1132 | */
1133 | function Tag(tc, text, a, v, w, h, col, bcol, bradius, boutline, bothickness,
1134 | font, padding, original) {
1135 | this.tc = tc;
1136 | this.image = null;
1137 | this.text = text;
1138 | this.text_original = original;
1139 | this.line_widths = [];
1140 | this.title = a.title || null;
1141 | this.a = a;
1142 | this.position = new Vector(v[0], v[1], v[2]);
1143 | this.x = this.y = this.z = 0;
1144 | this.w = w;
1145 | this.h = h;
1146 | this.colour = col || tc.textColour;
1147 | this.bgColour = bcol || tc.bgColour;
1148 | this.bgRadius = bradius | 0;
1149 | this.bgOutline = boutline || this.colour;
1150 | this.bgOutlineThickness = bothickness | 0;
1151 | this.textFont = font || tc.textFont;
1152 | this.padding = padding | 0;
1153 | this.sc = this.alpha = 1;
1154 | this.weighted = !tc.weight;
1155 | this.outline = new Outline(tc,this);
1156 | }
1157 | Tproto = Tag.prototype;
1158 | Tproto.Init = function(e) {
1159 | var tc = this.tc;
1160 | this.textHeight = tc.textHeight;
1161 | if(this.HasText()) {
1162 | this.Measure(tc.ctxt,tc);
1163 | } else {
1164 | this.w = this.iw;
1165 | this.h = this.ih;
1166 | }
1167 |
1168 | this.SetShadowColour = tc.shadowAlpha ? this.SetShadowColourAlpha : this.SetShadowColourFixed;
1169 | this.SetDraw(tc);
1170 | };
1171 | Tproto.Draw = Nop;
1172 | Tproto.HasText = function() {
1173 | return this.text && this.text[0].length > 0;
1174 | };
1175 | Tproto.EqualTo = function(e) {
1176 | var i = e.getElementsByTagName('img');
1177 | if(this.a.href != e.href)
1178 | return 0;
1179 | if(i.length)
1180 | return this.image.src == i[0].src;
1181 | return (e.innerText || e.textContent) == this.text_original;
1182 | };
1183 | Tproto.SetImage = function(i) {
1184 | this.image = this.fimage = i;
1185 | };
1186 | Tproto.SetDraw = function(t) {
1187 | this.Draw = this.fimage ? (t.ie > 7 ? this.DrawImageIE : this.DrawImage) : this.DrawText;
1188 | t.noSelect && (this.CheckActive = Nop);
1189 | };
1190 | Tproto.MeasureText = function(c) {
1191 | var i, l = this.text.length, w = 0, wl;
1192 | for(i = 0; i < l; ++i) {
1193 | this.line_widths[i] = wl = c.measureText(this.text[i]).width;
1194 | w = max(w, wl);
1195 | }
1196 | return w;
1197 | };
1198 | Tproto.Measure = function(c,t) {
1199 | var extents = FindTextBoundingBox(this.text, this.textFont, this.textHeight),
1200 | s, th, f, soff, cw, twidth, theight, img, tcv;
1201 | // add the gap at the top to the height to make equal gap at bottom
1202 | theight = extents ? extents.max.y + extents.min.y : this.textHeight;
1203 | c.font = this.font = this.textHeight + 'px ' + this.textFont;
1204 | twidth = this.MeasureText(c);
1205 | if(t.txtOpt) {
1206 | s = t.txtScale;
1207 | th = s * this.textHeight;
1208 | f = th + 'px ' + this.textFont;
1209 | soff = [s * t.shadowOffset[0], s * t.shadowOffset[1]];
1210 | c.font = f;
1211 | cw = this.MeasureText(c);
1212 | tcv = new TextCanvas(this.text, f, cw + s, (s * theight) + s, cw,
1213 | this.line_widths, t.textAlign, t.textVAlign, s);
1214 |
1215 | if(this.image)
1216 | tcv.SetImage(this.image, this.iw, this.ih, t.imagePosition, t.imagePadding,
1217 | t.imageAlign, t.imageVAlign, t.imageScale);
1218 |
1219 | img = tcv.Create(this.colour, this.bgColour, this.bgOutline,
1220 | s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1221 | s * this.padding, s * this.bgRadius);
1222 |
1223 | // add outline image using highlight colour
1224 | if(t.outlineMethod == 'colour') {
1225 | this.oimage = tcv.Create(this.outline.colour, this.bgColour, this.outline.colour,
1226 | s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1227 | s * this.padding, s * this.bgRadius);
1228 |
1229 | } else if(t.outlineMethod == 'size') {
1230 | extents = FindTextBoundingBox(this.text, this.textFont,
1231 | this.textHeight + t.outlineIncrease);
1232 | th = extents.max.y + extents.min.y;
1233 | f = (s * (this.textHeight + t.outlineIncrease)) + 'px ' + this.textFont;
1234 | c.font = f;
1235 | cw = this.MeasureText(c);
1236 |
1237 | tcv = new TextCanvas(this.text, f, cw + s, (s * th) + s, cw,
1238 | this.line_widths, t.textAlign, t.textVAlign, s);
1239 | if(this.image)
1240 | tcv.SetImage(this.image, this.iw + t.outlineIncrease,
1241 | this.ih + t.outlineIncrease, t.imagePosition, t.imagePadding,
1242 | t.imageAlign, t.imageVAlign, t.imageScale);
1243 |
1244 | this.oimage = tcv.Create(this.colour, this.bgColour, this.bgOutline,
1245 | s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
1246 | s * this.padding, s * this.bgRadius);
1247 |
1248 | this.oscale = this.oimage.width / img.width;
1249 | if(t.outlineIncrease > 0)
1250 | img = ExpandImage(img, this.oimage.width, this.oimage.height);
1251 | else
1252 | this.oimage = ExpandImage(this.oimage, img.width, img.height);
1253 | }
1254 | if(img) {
1255 | this.fimage = img;
1256 | twidth = this.fimage.width / s;
1257 | theight = this.fimage.height / s;
1258 | }
1259 | this.SetDraw(t);
1260 | t.txtOpt = !!this.fimage;
1261 | }
1262 | this.h = theight;
1263 | this.w = twidth;
1264 | };
1265 | Tproto.SetFont = function(f, c, bc, boc) {
1266 | this.textFont = f;
1267 | this.colour = c;
1268 | this.bgColour = bc;
1269 | this.bgOutline = boc;
1270 | this.Measure(this.tc.ctxt, this.tc);
1271 | };
1272 | Tproto.SetWeight = function(w) {
1273 | var tc = this.tc, modes = tc.weightMode.split(/[, ]/), m, s, wl = w.length;
1274 | if(!this.HasText())
1275 | return;
1276 | this.weighted = true;
1277 | for(s = 0; s < wl; ++s) {
1278 | m = modes[s] || 'size';
1279 | if('both' == m) {
1280 | this.Weight(w[s], tc.ctxt, tc, 'size', tc.min_weight[s],
1281 | tc.max_weight[s], s);
1282 | this.Weight(w[s], tc.ctxt, tc, 'colour', tc.min_weight[s],
1283 | tc.max_weight[s], s);
1284 | } else {
1285 | this.Weight(w[s], tc.ctxt, tc, m, tc.min_weight[s], tc.max_weight[s], s);
1286 | }
1287 | }
1288 | this.Measure(tc.ctxt, tc);
1289 | };
1290 | Tproto.Weight = function(w, c, t, m, wmin, wmax, wnum) {
1291 | w = isNaN(w) ? 1 : w;
1292 | var nweight = (w - wmin) / (wmax - wmin);
1293 | if('colour' == m)
1294 | this.colour = FindGradientColour(t, nweight, wnum);
1295 | else if('bgcolour' == m)
1296 | this.bgColour = FindGradientColour(t, nweight, wnum);
1297 | else if('bgoutline' == m)
1298 | this.bgOutline = FindGradientColour(t, nweight, wnum);
1299 | else if('outline' == m)
1300 | this.outline.colour = FindGradientColour(t, nweight, wnum);
1301 | else if('size' == m) {
1302 | if(t.weightSizeMin > 0 && t.weightSizeMax > t.weightSizeMin) {
1303 | this.textHeight = t.weightSize *
1304 | (t.weightSizeMin + (t.weightSizeMax - t.weightSizeMin) * nweight);
1305 | } else {
1306 | // min textHeight of 1
1307 | this.textHeight = max(1, w * t.weightSize);
1308 | }
1309 | }
1310 | };
1311 | Tproto.SetShadowColourFixed = function(c,s,a) {
1312 | c.shadowColor = s;
1313 | };
1314 | Tproto.SetShadowColourAlpha = function(c,s,a) {
1315 | c.shadowColor = SetAlpha(s, a);
1316 | };
1317 | Tproto.DrawText = function(c,xoff,yoff) {
1318 | var t = this.tc, x = this.x, y = this.y, s = this.sc, i, xl;
1319 | c.globalAlpha = this.alpha;
1320 | c.fillStyle = this.colour;
1321 | t.shadow && this.SetShadowColour(c,t.shadow,this.alpha);
1322 | c.font = this.font;
1323 | x += xoff / s;
1324 | y += (yoff / s) - (this.h / 2);
1325 | for(i = 0; i < this.text.length; ++i) {
1326 | xl = x;
1327 | if('right' == t.textAlign) {
1328 | xl += this.w / 2 - this.line_widths[i];
1329 | } else if('centre' == t.textAlign) {
1330 | xl -= this.line_widths[i] / 2;
1331 | } else {
1332 | xl -= this.w / 2;
1333 | }
1334 | c.setTransform(s, 0, 0, s, s * xl, s * y);
1335 | c.fillText(this.text[i], 0, 0);
1336 | y += this.textHeight;
1337 | }
1338 | };
1339 | Tproto.DrawImage = function(c,xoff,yoff,im) {
1340 | var x = this.x, y = this.y, s = this.sc,
1341 | i = im || this.fimage, w = this.w, h = this.h, a = this.alpha,
1342 | shadow = this.shadow;
1343 | c.globalAlpha = a;
1344 | shadow && this.SetShadowColour(c,shadow,a);
1345 | x += (xoff / s) - (w / 2);
1346 | y += (yoff / s) - (h / 2);
1347 | c.setTransform(s, 0, 0, s, s * x, s * y);
1348 | c.drawImage(i, 0, 0, w, h);
1349 | };
1350 | Tproto.DrawImageIE = function(c,xoff,yoff) {
1351 | var i = this.fimage, s = this.sc,
1352 | w = i.width = this.w*s, h = i.height = this.h * s,
1353 | x = (this.x*s) + xoff - (w/2), y = (this.y*s) + yoff - (h/2);
1354 | c.setTransform(1,0,0,1,0,0);
1355 | c.globalAlpha = this.alpha;
1356 | c.drawImage(i, x, y);
1357 | };
1358 | Tproto.Calc = function(m,a) {
1359 | var pp, t = this.tc, mnb = t.minBrightness,
1360 | mxb = t.maxBrightness, r = t.max_radius;
1361 | pp = m.xform(this.position);
1362 | this.xformed = pp;
1363 | pp = Project(t, pp, t.stretchX, t.stretchY);
1364 | this.x = pp.x;
1365 | this.y = pp.y;
1366 | this.z = pp.z;
1367 | this.sc = pp.w;
1368 | this.alpha = a * Clamp(mnb + (mxb - mnb) * (r - this.z) / (2 * r), 0, 1);
1369 | return this.xformed;
1370 | };
1371 | Tproto.UpdateActive = function(c, xoff, yoff) {
1372 | var o = this.outline, w = this.w, h = this.h,
1373 | x = this.x - w/2, y = this.y - h/2;
1374 | o.Update(x, y, w, h, this.sc, this.z, xoff, yoff);
1375 | return o;
1376 | };
1377 | Tproto.CheckActive = function(c,xoff,yoff) {
1378 | var t = this.tc, o = this.UpdateActive(c, xoff, yoff);
1379 | return o.Active(c, t.mx, t.my) ? o : null;
1380 | };
1381 | Tproto.Clicked = function(e) {
1382 | var a = this.a, t = a.target, h = a.href, evt;
1383 | if(t != '' && t != '_self') {
1384 | if(self.frames[t]) {
1385 | self.frames[t].document.location = h;
1386 | } else{
1387 | try {
1388 | if(top.frames[t]) {
1389 | top.frames[t].document.location = h;
1390 | return;
1391 | }
1392 | } catch(err) {
1393 | // different domain/port/protocol?
1394 | }
1395 | window.open(h, t);
1396 | }
1397 | return;
1398 | }
1399 | if(doc.createEvent) {
1400 | evt = doc.createEvent('MouseEvents');
1401 | evt.initMouseEvent('click', 1, 1, window, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
1402 | if(!a.dispatchEvent(evt))
1403 | return;
1404 | } else if(a.fireEvent) {
1405 | if(!a.fireEvent('onclick'))
1406 | return;
1407 | }
1408 | doc.location = h;
1409 | };
1410 | /**
1411 | * @constructor
1412 | */
1413 | function TagCanvas(cid,lctr,opt) {
1414 | var i, p, c = doc.getElementById(cid), cp = ['id','class','innerHTML'], raf;
1415 |
1416 | if(!c) throw 0;
1417 | if(Defined(window.G_vmlCanvasManager)) {
1418 | c = window.G_vmlCanvasManager.initElement(c);
1419 | this.ie = parseFloat(navigator.appVersion.split('MSIE')[1]);
1420 | }
1421 | if(c && (!c.getContext || !c.getContext('2d').fillText)) {
1422 | p = doc.createElement('DIV');
1423 | for(i = 0; i < cp.length; ++i)
1424 | p[cp[i]] = c[cp[i]];
1425 | c.parentNode.insertBefore(p,c);
1426 | c.parentNode.removeChild(c);
1427 | throw 0;
1428 | }
1429 | for(i in TagCanvas.options)
1430 | this[i] = opt && Defined(opt[i]) ? opt[i] :
1431 | (Defined(TagCanvas[i]) ? TagCanvas[i] : TagCanvas.options[i]);
1432 |
1433 | this.canvas = c;
1434 | this.ctxt = c.getContext('2d');
1435 | this.z1 = 250 / max(this.depth, 0.001);
1436 | this.z2 = this.z1 / this.zoom;
1437 | this.radius = min(c.height, c.width) * 0.0075; // fits radius of 100 in canvas
1438 | this.max_radius = 100;
1439 | this.max_weight = [];
1440 | this.min_weight = [];
1441 | this.textFont = this.textFont && FixFont(this.textFont);
1442 | this.textHeight *= 1;
1443 | this.imageRadius = this.imageRadius.toString();
1444 | this.pulsateTo = Clamp(this.pulsateTo, 0, 1);
1445 | this.minBrightness = Clamp(this.minBrightness, 0, 1);
1446 | this.maxBrightness = Clamp(this.maxBrightness, this.minBrightness, 1);
1447 | this.ctxt.textBaseline = 'top';
1448 | this.lx = (this.lock + '').indexOf('x') + 1;
1449 | this.ly = (this.lock + '').indexOf('y') + 1;
1450 | this.frozen = this.dx = this.dy = this.fixedAnim = this.touchState = 0;
1451 | this.fixedAlpha = 1;
1452 | this.source = lctr || cid;
1453 | this.repeatTags = min(64, ~~this.repeatTags);
1454 | this.minTags = min(200, ~~this.minTags);
1455 | if(~~this.scrollPause > 0)
1456 | TagCanvas.scrollPause = ~~this.scrollPause;
1457 | else
1458 | this.scrollPause = 0;
1459 | if(this.minTags > 0 && this.repeatTags < 1 && (i = this.GetTags().length))
1460 | this.repeatTags = ceil(this.minTags / i) - 1;
1461 | this.transform = Matrix.Identity();
1462 | this.startTime = this.time = TimeNow();
1463 | this.mx = this.my = -1;
1464 | this.centreImage && CentreImage(this);
1465 | this.Animate = this.dragControl ? this.AnimateDrag : this.AnimatePosition;
1466 | this.animTiming = (typeof TagCanvas[this.animTiming] == 'function' ?
1467 | TagCanvas[this.animTiming] : TagCanvas.Smooth);
1468 | if(this.shadowBlur || this.shadowOffset[0] || this.shadowOffset[1]) {
1469 | // let the browser translate "red" into "#ff0000"
1470 | this.ctxt.shadowColor = this.shadow;
1471 | this.shadow = this.ctxt.shadowColor;
1472 | this.shadowAlpha = ShadowAlphaBroken();
1473 | } else {
1474 | delete this.shadow;
1475 | }
1476 | this.Load();
1477 | if(lctr && this.hideTags) {
1478 | (function(t) {
1479 | if(TagCanvas.loaded)
1480 | t.HideTags();
1481 | else
1482 | AddHandler('load', function() { t.HideTags(); }, window);
1483 | })(this);
1484 | }
1485 |
1486 | this.yaw = this.initial ? this.initial[0] * this.maxSpeed : 0;
1487 | this.pitch = this.initial ? this.initial[1] * this.maxSpeed : 0;
1488 | if(this.tooltip) {
1489 | this.ctitle = c.title;
1490 | c.title = '';
1491 | if(this.tooltip == 'native') {
1492 | this.Tooltip = this.TooltipNative;
1493 | } else {
1494 | this.Tooltip = this.TooltipDiv;
1495 | if(!this.ttdiv) {
1496 | this.ttdiv = doc.createElement('div');
1497 | this.ttdiv.className = this.tooltipClass;
1498 | this.ttdiv.style.position = 'absolute';
1499 | this.ttdiv.style.zIndex = c.style.zIndex + 1;
1500 | AddHandler('mouseover',function(e){e.target.style.display='none';},this.ttdiv);
1501 | doc.body.appendChild(this.ttdiv);
1502 | }
1503 | }
1504 | } else {
1505 | this.Tooltip = this.TooltipNone;
1506 | }
1507 | if(!this.noMouse && !handlers[cid]) {
1508 | handlers[cid] = [
1509 | ['mousemove', MouseMove],
1510 | ['mouseout', MouseOut],
1511 | ['mouseup', MouseUp],
1512 | ['touchstart', TouchDown],
1513 | ['touchend', TouchUp],
1514 | ['touchcancel', TouchUp],
1515 | ['touchmove', TouchMove]
1516 | ];
1517 | if(this.dragControl) {
1518 | handlers[cid].push(['mousedown', MouseDown]);
1519 | handlers[cid].push(['selectstart', Nop]);
1520 | }
1521 | if(this.wheelZoom) {
1522 | handlers[cid].push(['mousewheel', MouseWheel]);
1523 | handlers[cid].push(['DOMMouseScroll', MouseWheel]);
1524 | }
1525 | if(this.scrollPause) {
1526 | handlers[cid].push(['scroll', Scroll, window]);
1527 | }
1528 | for(i = 0; i < handlers[cid].length; ++i) {
1529 | p = handlers[cid][i];
1530 | AddHandler(p[0], p[1], p[2] ? p[2] : c);
1531 | }
1532 | }
1533 | if(!TagCanvas.started) {
1534 | raf = window.requestAnimationFrame = window.requestAnimationFrame ||
1535 | window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
1536 | window.msRequestAnimationFrame;
1537 | TagCanvas.NextFrame = raf ? TagCanvas.NextFrameRAF :
1538 | TagCanvas.NextFrameTimeout;
1539 | TagCanvas.interval = this.interval;
1540 | TagCanvas.NextFrame(this.interval);
1541 | TagCanvas.started = 1;
1542 | }
1543 | }
1544 | TCproto = TagCanvas.prototype;
1545 | TCproto.SourceElements = function() {
1546 | if(doc.querySelectorAll)
1547 | return doc.querySelectorAll('#' + this.source);
1548 | return [doc.getElementById(this.source)];
1549 | };
1550 | TCproto.HideTags = function() {
1551 | var el = this.SourceElements(), i;
1552 | for(i = 0; i < el.length; ++i)
1553 | el[i].style.display = 'none';
1554 | };
1555 | TCproto.GetTags = function() {
1556 | var el = this.SourceElements(), etl, tl = [], i, j, k;
1557 | for(k = 0; k <= this.repeatTags; ++k) {
1558 | for(i = 0; i < el.length; ++i) {
1559 | etl = el[i].getElementsByTagName('a');
1560 | for(j = 0; j < etl.length; ++j) {
1561 | tl.push(etl[j]);
1562 | }
1563 | }
1564 | }
1565 | return tl;
1566 | };
1567 | TCproto.Message = function(text) {
1568 | var tl = [], i, p, tc = text.split(''), a, t, x, z;
1569 | for(i = 0; i < tc.length; ++i) {
1570 | if(tc[i] != ' ') {
1571 | p = i - tc.length / 2;
1572 | a = doc.createElement('A');
1573 | a.href = '#';
1574 | a.innerText = tc[i];
1575 | x = 100 * sin(p / 9);
1576 | z = -100 * cos(p / 9);
1577 | t = new Tag(this, tc[i], a, [x,0,z], 2, 18, '#000', '#fff', 0, 0, 0,
1578 | 'monospace', 2, tc[i]);
1579 | t.Init();
1580 | tl.push(t);
1581 | }
1582 | }
1583 | return tl;
1584 | };
1585 | TCproto.CreateTag = function(e) {
1586 | var im, i, t, txt, ts, font, bc, boc, p = [0, 0, 0];
1587 | if('text' != this.imageMode) {
1588 | im = e.getElementsByTagName('img');
1589 | if(im.length) {
1590 | i = new Image;
1591 | i.src = im[0].src;
1592 |
1593 | if(!this.imageMode) {
1594 | t = new Tag(this, "", e, p, 0, 0);
1595 | t.SetImage(i);
1596 | //t.Init();
1597 | AddImage(i, im[0], t, this);
1598 | return t;
1599 | }
1600 | }
1601 | }
1602 | if('image' != this.imageMode) {
1603 | ts = new TextSplitter(e);
1604 | txt = ts.Lines();
1605 | if(!ts.Empty()) {
1606 | font = this.textFont || FixFont(GetProperty(e,'font-family'));
1607 | if(this.splitWidth)
1608 | txt = ts.SplitWidth(this.splitWidth, this.ctxt, font, this.textHeight);
1609 |
1610 | bc = this.bgColour == 'tag' ? GetProperty(e, 'background-color') :
1611 | this.bgColour;
1612 | boc = this.bgOutline == 'tag' ? GetProperty(e, 'color') : this.bgOutline;
1613 | } else {
1614 | ts = null;
1615 | }
1616 | }
1617 | if(ts || i) {
1618 | t = new Tag(this, txt, e, p, 2, this.textHeight + 2,
1619 | this.textColour || GetProperty(e,'color'), bc, this.bgRadius,
1620 | boc, this.bgOutlineThickness, font, this.padding, ts && ts.original);
1621 | if(i) {
1622 | t.SetImage(i);
1623 | AddImage(i, im[0], t, this);
1624 | } else {
1625 | t.Init();
1626 | }
1627 | return t;
1628 | }
1629 | };
1630 | TCproto.UpdateTag = function(t, a) {
1631 | var colour = this.textColour || GetProperty(a, 'color'),
1632 | font = this.textFont || FixFont(GetProperty(a, 'font-family')),
1633 | bc = this.bgColour == 'tag' ? GetProperty(a, 'background-color') :
1634 | this.bgColour, boc = this.bgOutline == 'tag' ? GetProperty(a, 'color') :
1635 | this.bgOutline;
1636 | t.a = a;
1637 | t.title = a.title;
1638 | if(t.colour != colour || t.textFont != font || t.bgColour != bc ||
1639 | t.bgOutline != boc)
1640 | t.SetFont(font, colour, bc, boc);
1641 | };
1642 | TCproto.Weight = function(tl) {
1643 | var ll = tl.length, w, i, s, weights = [], valid,
1644 | wfrom = this.weightFrom ? this.weightFrom.split(/[, ]/) : [null],
1645 | wl = wfrom.length;
1646 | for(i = 0; i < ll; ++i) {
1647 | weights[i] = [];
1648 | for(s = 0; s < wl; ++s) {
1649 | w = FindWeight(tl[i].a, wfrom[s], this.textHeight);
1650 | if(!this.max_weight[s] || w > this.max_weight[s])
1651 | this.max_weight[s] = w;
1652 | if(!this.min_weight[s] || w < this.min_weight[s])
1653 | this.min_weight[s] = w;
1654 | weights[i][s] = w;
1655 | }
1656 | }
1657 | for(s = 0; s < wl; ++s) {
1658 | if(this.max_weight[s] > this.min_weight[s])
1659 | valid = 1;
1660 | }
1661 | if(valid) {
1662 | for(i = 0; i < ll; ++i) {
1663 | tl[i].SetWeight(weights[i]);
1664 | }
1665 | }
1666 | };
1667 | TCproto.Load = function() {
1668 | var tl = this.GetTags(), taglist = [], shape, t,
1669 | shapeArgs, rx, ry, rz, vl, i, tagmap = [], pfuncs = {
1670 | sphere: PointsOnSphere,
1671 | vcylinder: PointsOnCylinderV,
1672 | hcylinder: PointsOnCylinderH,
1673 | vring: PointsOnRingV,
1674 | hring: PointsOnRingH
1675 | };
1676 |
1677 | if(tl.length) {
1678 | tagmap.length = tl.length;
1679 | for(i = 0; i < tl.length; ++i)
1680 | tagmap[i] = i;
1681 | this.shuffleTags && Shuffle(tagmap);
1682 | rx = 100 * this.radiusX;
1683 | ry = 100 * this.radiusY;
1684 | rz = 100 * this.radiusZ;
1685 | this.max_radius = max(rx, max(ry, rz));
1686 |
1687 | for(i = 0; i < tl.length; ++i) {
1688 | t = this.CreateTag(tl[tagmap[i]]);
1689 | if(t)
1690 | taglist.push(t);
1691 | }
1692 | this.weight && this.Weight(taglist, true);
1693 |
1694 | if(this.shapeArgs) {
1695 | this.shapeArgs[0] = taglist.length;
1696 | } else {
1697 | shapeArgs = this.shape.toString().split(/[(),]/);
1698 | shape = shapeArgs.shift();
1699 | if(typeof window[shape] === 'function')
1700 | this.shape = window[shape];
1701 | else
1702 | this.shape = pfuncs[shape] || pfuncs.sphere;
1703 | this.shapeArgs = [taglist.length, rx, ry, rz].concat(shapeArgs);
1704 | }
1705 | vl = this.shape.apply(this, this.shapeArgs);
1706 | this.listLength = taglist.length;
1707 | for(i = 0; i < taglist.length; ++i)
1708 | taglist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
1709 | }
1710 | if(this.noTagsMessage && !taglist.length) {
1711 | i = (this.imageMode && this.imageMode != 'both' ? this.imageMode + ' ': '');
1712 | taglist = this.Message('No ' + i + 'tags');
1713 | }
1714 | this.taglist = taglist;
1715 | };
1716 | TCproto.Update = function() {
1717 | var tl = this.GetTags(), newlist = [],
1718 | taglist = this.taglist, found,
1719 | added = [], removed = [], vl, ol, nl, i, j;
1720 |
1721 | if(!this.shapeArgs)
1722 | return this.Load();
1723 |
1724 | if(tl.length) {
1725 | nl = this.listLength = tl.length;
1726 | ol = taglist.length;
1727 |
1728 | // copy existing list, populate "removed"
1729 | for(i = 0; i < ol; ++i) {
1730 | newlist.push(taglist[i]);
1731 | removed.push(i);
1732 | }
1733 |
1734 | // find added and removed tags
1735 | for(i = 0; i < nl; ++i) {
1736 | for(j = 0, found = 0; j < ol; ++j) {
1737 | if(taglist[j].EqualTo(tl[i])) {
1738 | this.UpdateTag(newlist[j], tl[i]);
1739 | found = removed[j] = -1;
1740 | }
1741 | }
1742 | if(!found)
1743 | added.push(i);
1744 | }
1745 |
1746 | // clean out found tags from removed list
1747 | for(i = 0, j = 0; i < ol; ++i) {
1748 | if(removed[j] == -1)
1749 | removed.splice(j,1);
1750 | else
1751 | ++j;
1752 | }
1753 |
1754 | // insert new tags in gaps where old tags removed
1755 | if(removed.length) {
1756 | Shuffle(removed);
1757 | while(removed.length && added.length) {
1758 | i = removed.shift();
1759 | j = added.shift();
1760 | newlist[i] = this.CreateTag(tl[j]);
1761 | }
1762 |
1763 | // remove any more (in reverse order)
1764 | removed.sort(function(a,b) {return a-b});
1765 | while(removed.length) {
1766 | newlist.splice(removed.pop(), 1);
1767 | }
1768 | }
1769 |
1770 | // add any extra tags
1771 | j = newlist.length / (added.length + 1);
1772 | i = 0;
1773 | while(added.length) {
1774 | newlist.splice(ceil(++i * j), 0, this.CreateTag(tl[added.shift()]));
1775 | }
1776 |
1777 | // assign correct positions to tags
1778 | this.shapeArgs[0] = nl = newlist.length;
1779 | vl = this.shape.apply(this, this.shapeArgs);
1780 | for(i = 0; i < nl; ++i)
1781 | newlist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
1782 |
1783 | // reweight tags
1784 | this.weight && this.Weight(newlist);
1785 | }
1786 | this.taglist = newlist;
1787 | };
1788 | TCproto.SetShadow = function(c) {
1789 | c.shadowBlur = this.shadowBlur;
1790 | c.shadowOffsetX = this.shadowOffset[0];
1791 | c.shadowOffsetY = this.shadowOffset[1];
1792 | };
1793 | TCproto.Draw = function(t) {
1794 | if(this.paused)
1795 | return;
1796 | var cv = this.canvas, cw = cv.width, ch = cv.height, max_sc = 0,
1797 | tdelta = (t - this.time) * TagCanvas.interval / 1000,
1798 | x = cw / 2 + this.offsetX, y = ch / 2 + this.offsetY, c = this.ctxt,
1799 | active, a, i, aindex = -1, tl = this.taglist, l = tl.length,
1800 | frontsel = this.frontSelect, centreDrawn = (this.centreFunc == Nop), fixed;
1801 | this.time = t;
1802 | if(this.frozen && this.drawn)
1803 | return this.Animate(cw,ch,tdelta);
1804 | fixed = this.AnimateFixed();
1805 | c.setTransform(1,0,0,1,0,0);
1806 | for(i = 0; i < l; ++i)
1807 | tl[i].Calc(this.transform, this.fixedAlpha);
1808 | tl = SortList(tl, function(a,b) {return b.z-a.z});
1809 |
1810 | if(fixed && this.fixedAnim.active) {
1811 | active = this.fixedAnim.tag.UpdateActive(c, x, y);
1812 | } else {
1813 | this.active = null;
1814 | for(i = 0; i < l; ++i) {
1815 | a = this.mx >= 0 && this.my >= 0 && this.taglist[i].CheckActive(c, x, y);
1816 | if(a && a.sc > max_sc && (!frontsel || a.z <= 0)) {
1817 | active = a;
1818 | aindex = i;
1819 | active.tag = this.taglist[i];
1820 | max_sc = a.sc;
1821 | }
1822 | }
1823 | this.active = active;
1824 | }
1825 |
1826 | this.txtOpt || (this.shadow && this.SetShadow(c));
1827 | c.clearRect(0,0,cw,ch);
1828 | for(i = 0; i < l; ++i) {
1829 | if(!centreDrawn && tl[i].z <= 0) {
1830 | // run the centreFunc if the next tag is at the front
1831 | try { this.centreFunc(c, cw, ch, x, y); }
1832 | catch(e) {
1833 | alert(e);
1834 | // don't run it again
1835 | this.centreFunc = Nop;
1836 | }
1837 | centreDrawn = true;
1838 | }
1839 |
1840 | if(!(active && active.tag == tl[i] && active.PreDraw(c, tl[i], x, y)))
1841 | tl[i].Draw(c, x, y);
1842 | active && active.tag == tl[i] && active.PostDraw(c);
1843 | }
1844 | if(this.freezeActive && active) {
1845 | this.Freeze();
1846 | } else {
1847 | this.UnFreeze();
1848 | this.drawn = (l == this.listLength);
1849 | }
1850 | if(this.fixedCallback) {
1851 | this.fixedCallback(this,this.fixedCallbackTag);
1852 | this.fixedCallback = null;
1853 | }
1854 | fixed || this.Animate(cw, ch, tdelta);
1855 | active && active.LastDraw(c);
1856 | cv.style.cursor = active ? this.activeCursor : '';
1857 | this.Tooltip(active,this.taglist[aindex]);
1858 | };
1859 | TCproto.TooltipNone = function() { };
1860 | TCproto.TooltipNative = function(active,tag) {
1861 | if(active)
1862 | this.canvas.title = tag && tag.title ? tag.title : '';
1863 | else
1864 | this.canvas.title = this.ctitle;
1865 | };
1866 | TCproto.SetTTDiv = function(title, tag) {
1867 | var tc = this, s = tc.ttdiv.style;
1868 | if(title != tc.ttdiv.innerHTML)
1869 | s.display = 'none';
1870 | tc.ttdiv.innerHTML = title;
1871 | tag && (tag.title = tc.ttdiv.innerHTML);
1872 | if(s.display == 'none' && ! tc.tttimer) {
1873 | tc.tttimer = setTimeout(function() {
1874 | var p = AbsPos(tc.canvas.id);
1875 | s.display = 'block';
1876 | s.left = p.x + tc.mx + 'px';
1877 | s.top = p.y + tc.my + 24 + 'px';
1878 | tc.tttimer = null;
1879 | }, tc.tooltipDelay);
1880 | }
1881 | };
1882 | TCproto.TooltipDiv = function(active,tag) {
1883 | if(active && tag && tag.title) {
1884 | this.SetTTDiv(tag.title, tag);
1885 | } else if(!active && this.mx != -1 && this.my != -1 && this.ctitle.length) {
1886 | this.SetTTDiv(this.ctitle);
1887 | } else {
1888 | this.ttdiv.style.display = 'none';
1889 | }
1890 | };
1891 | TCproto.Transform = function(tc, p, y) {
1892 | if(p || y) {
1893 | var sp = sin(p), cp = cos(p), sy = sin(y), cy = cos(y),
1894 | ym = new Matrix([cy,0,sy, 0,1,0, -sy,0,cy]),
1895 | pm = new Matrix([1,0,0, 0,cp,-sp, 0,sp,cp]);
1896 | tc.transform = tc.transform.mul(ym.mul(pm));
1897 | }
1898 | };
1899 | TCproto.AnimateFixed = function() {
1900 | var fa, t1, angle, m, d;
1901 | if(this.fadeIn) {
1902 | t1 = TimeNow() - this.startTime;
1903 | if(t1 >= this.fadeIn) {
1904 | this.fadeIn = 0;
1905 | this.fixedAlpha = 1;
1906 | } else {
1907 | this.fixedAlpha = t1 / this.fadeIn;
1908 | }
1909 | }
1910 | if(this.fixedAnim) {
1911 | if(!this.fixedAnim.transform)
1912 | this.fixedAnim.transform = this.transform;
1913 | fa = this.fixedAnim, t1 = TimeNow() - fa.t0, angle = fa.angle,
1914 | m, d = this.animTiming(fa.t, t1);
1915 | this.transform = fa.transform;
1916 | if(t1 >= fa.t) {
1917 | this.fixedCallbackTag = fa.tag;
1918 | this.fixedCallback = fa.cb;
1919 | this.fixedAnim = this.yaw = this.pitch = 0;
1920 | } else {
1921 | angle *= d;
1922 | }
1923 | m = Matrix.Rotation(angle, fa.axis);
1924 | this.transform = this.transform.mul(m);
1925 | return (this.fixedAnim != 0);
1926 | }
1927 | return false;
1928 | };
1929 | TCproto.AnimatePosition = function(w, h, t) {
1930 | var tc = this, x = tc.mx, y = tc.my, s, r;
1931 | if(!tc.frozen && x >= 0 && y >= 0 && x < w && y < h) {
1932 | s = tc.maxSpeed, r = tc.reverse ? -1 : 1;
1933 | tc.lx || (tc.yaw = ((x * 2 * s / w) - s) * r * t);
1934 | tc.ly || (tc.pitch = ((y * 2 * s / h) - s) * -r * t);
1935 | tc.initial = null;
1936 | } else if(!tc.initial) {
1937 | if(tc.frozen && !tc.freezeDecel)
1938 | tc.yaw = tc.pitch = 0;
1939 | else
1940 | tc.Decel(tc);
1941 | }
1942 | this.Transform(tc, tc.pitch, tc.yaw);
1943 | };
1944 | TCproto.AnimateDrag = function(w, h, t) {
1945 | var tc = this, rs = 100 * t * tc.maxSpeed / tc.max_radius / tc.zoom;
1946 | if(tc.dx || tc.dy) {
1947 | tc.lx || (tc.yaw = tc.dx * rs / tc.stretchX);
1948 | tc.ly || (tc.pitch = tc.dy * -rs / tc.stretchY);
1949 | tc.dx = tc.dy = 0;
1950 | tc.initial = null;
1951 | } else if(!tc.initial) {
1952 | tc.Decel(tc);
1953 | }
1954 | this.Transform(tc, tc.pitch, tc.yaw);
1955 | };
1956 | TCproto.Freeze = function() {
1957 | if(!this.frozen) {
1958 | this.preFreeze = [this.yaw, this.pitch];
1959 | this.frozen = 1;
1960 | this.drawn = 0;
1961 | }
1962 | };
1963 | TCproto.UnFreeze = function() {
1964 | if(this.frozen) {
1965 | this.yaw = this.preFreeze[0];
1966 | this.pitch = this.preFreeze[1];
1967 | this.frozen = 0;
1968 | }
1969 | };
1970 | TCproto.Decel = function(tc) {
1971 | var s = tc.minSpeed, ay = abs(tc.yaw), ap = abs(tc.pitch);
1972 | if(!tc.lx && ay > s)
1973 | tc.yaw = ay > tc.z0 ? tc.yaw * tc.decel : 0;
1974 | if(!tc.ly && ap > s)
1975 | tc.pitch = ap > tc.z0 ? tc.pitch * tc.decel : 0;
1976 | };
1977 | TCproto.Zoom = function(r) {
1978 | this.z2 = this.z1 * (1/r);
1979 | this.drawn = 0;
1980 | };
1981 | TCproto.Clicked = function(e) {
1982 | var a = this.active;
1983 | try {
1984 | if(a && a.tag)
1985 | if(this.clickToFront === false || this.clickToFront === null)
1986 | a.tag.Clicked(e);
1987 | else
1988 | this.TagToFront(a.tag, this.clickToFront, function() {
1989 | a.tag.Clicked(e);
1990 | }, true);
1991 | } catch(ex) {
1992 | }
1993 | };
1994 | TCproto.Wheel = function(i) {
1995 | var z = this.zoom + this.zoomStep * (i ? 1 : -1);
1996 | this.zoom = min(this.zoomMax,max(this.zoomMin,z));
1997 | this.Zoom(this.zoom);
1998 | };
1999 | TCproto.BeginDrag = function(e) {
2000 | this.down = EventXY(e, this.canvas);
2001 | e.cancelBubble = true;
2002 | e.returnValue = false;
2003 | e.preventDefault && e.preventDefault();
2004 | };
2005 | TCproto.Drag = function(e, p) {
2006 | if(this.dragControl && this.down) {
2007 | var t2 = this.dragThreshold * this.dragThreshold,
2008 | dx = p.x - this.down.x, dy = p.y - this.down.y;
2009 | if(this.dragging || dx * dx + dy * dy > t2) {
2010 | this.dx = dx;
2011 | this.dy = dy;
2012 | this.dragging = 1;
2013 | this.down = p;
2014 | }
2015 | }
2016 | return this.dragging;
2017 | };
2018 | TCproto.EndDrag = function() {
2019 | var res = this.dragging;
2020 | this.dragging = this.down = null;
2021 | return res;
2022 | };
2023 | function PinchDistance(e) {
2024 | var t1 = e.targetTouches[0], t2 = e.targetTouches[1];
2025 | return sqrt(pow(t2.pageX - t1.pageX, 2) + pow(t2.pageY - t1.pageY, 2));
2026 | }
2027 | TCproto.BeginPinch = function(e) {
2028 | this.pinched = [PinchDistance(e), this.zoom];
2029 | e.preventDefault && e.preventDefault();
2030 | };
2031 | TCproto.Pinch = function(e) {
2032 | var z, d, p = this.pinched;
2033 | if(!p)
2034 | return;
2035 | d = PinchDistance(e);
2036 | z = p[1] * d / p[0];
2037 | this.zoom = min(this.zoomMax,max(this.zoomMin,z));
2038 | this.Zoom(this.zoom);
2039 | };
2040 | TCproto.EndPinch = function(e) {
2041 | this.pinched = null;
2042 | };
2043 | TCproto.Pause = function() { this.paused = true; };
2044 | TCproto.Resume = function() { this.paused = false; };
2045 | TCproto.SetSpeed = function(i) {
2046 | this.initial = i;
2047 | this.yaw = i[0] * this.maxSpeed;
2048 | this.pitch = i[1] * this.maxSpeed;
2049 | };
2050 | TCproto.FindTag = function(t) {
2051 | if(!Defined(t))
2052 | return null;
2053 | Defined(t.index) && (t = t.index);
2054 | if(!IsObject(t))
2055 | return this.taglist[t];
2056 | var srch, tgt, i;
2057 | if(Defined(t.id))
2058 | srch = 'id', tgt = t.id;
2059 | else if(Defined(t.text))
2060 | srch = 'innerText', tgt = t.text;
2061 |
2062 | for(i = 0; i < this.taglist.length; ++i)
2063 | if(this.taglist[i].a[srch] == tgt)
2064 | return this.taglist[i];
2065 | };
2066 | TCproto.RotateTag = function(tag, lt, lg, time, callback, active) {
2067 | var t = tag.Calc(this.transform, 1), v1 = new Vector(t.x, t.y, t.z),
2068 | v2 = MakeVector(lg, lt), angle = v1.angle(v2), u = v1.cross(v2).unit();
2069 | if(angle == 0) {
2070 | this.fixedCallbackTag = tag;
2071 | this.fixedCallback = callback;
2072 | } else {
2073 | this.fixedAnim = {
2074 | angle: -angle,
2075 | axis: u,
2076 | t: time,
2077 | t0: TimeNow(),
2078 | cb: callback,
2079 | tag: tag,
2080 | active: active
2081 | };
2082 | }
2083 | };
2084 | TCproto.TagToFront = function(tag, time, callback, active) {
2085 | this.RotateTag(tag, 0, 0, time, callback, active);
2086 | };
2087 | TagCanvas.Start = function(id,l,o) {
2088 | TagCanvas.Delete(id);
2089 | TagCanvas.tc[id] = new TagCanvas(id,l,o);
2090 | };
2091 | function tccall(f,id) {
2092 | TagCanvas.tc[id] && TagCanvas.tc[id][f]();
2093 | }
2094 | TagCanvas.Linear = function(t, t0) { return t0 / t; }
2095 | TagCanvas.Smooth = function(t, t0) { return 0.5 - cos(t0 * Math.PI / t) / 2; }
2096 | TagCanvas.Pause = function(id) { tccall('Pause',id); };
2097 | TagCanvas.Resume = function(id) { tccall('Resume',id); };
2098 | TagCanvas.Reload = function(id) { tccall('Load',id); };
2099 | TagCanvas.Update = function(id) { tccall('Update',id); };
2100 | TagCanvas.SetSpeed = function(id, speed) {
2101 | if(IsObject(speed) && TagCanvas.tc[id] &&
2102 | !isNaN(speed[0]) && !isNaN(speed[1])) {
2103 | TagCanvas.tc[id].SetSpeed(speed);
2104 | return true;
2105 | }
2106 | return false;
2107 | };
2108 | TagCanvas.TagToFront = function(id, options) {
2109 | if(!IsObject(options))
2110 | return false;
2111 | options.lat = options.lng = 0;
2112 | return TagCanvas.RotateTag(id, options);
2113 | };
2114 | TagCanvas.RotateTag = function(id, options) {
2115 | if(IsObject(options) && TagCanvas.tc[id]) {
2116 | if(isNaN(options.time))
2117 | options.time = 500;
2118 | var tt = TagCanvas.tc[id].FindTag(options);
2119 | if(tt) {
2120 | TagCanvas.tc[id].RotateTag(tt, options.lat, options.lng,
2121 | options.time, options.callback, options.active);
2122 | return true;
2123 | }
2124 | }
2125 | return false;
2126 | };
2127 | TagCanvas.Delete = function(id) {
2128 | var i, c;
2129 | if(handlers[id]) {
2130 | c = doc.getElementById(id);
2131 | if(c) {
2132 | for(i = 0; i < handlers[id].length; ++i)
2133 | RemoveHandler(handlers[id][i][0], handlers[id][i][1], c);
2134 | }
2135 | }
2136 | delete handlers[id];
2137 | delete TagCanvas.tc[id];
2138 | };
2139 | TagCanvas.NextFrameRAF = function() {
2140 | requestAnimationFrame(DrawCanvasRAF);
2141 | };
2142 | TagCanvas.NextFrameTimeout = function(iv) {
2143 | setTimeout(DrawCanvas, iv);
2144 | };
2145 | TagCanvas.tc = {};
2146 | TagCanvas.options = {
2147 | z1: 20000,
2148 | z2: 20000,
2149 | z0: 0.0002,
2150 | freezeActive: true,
2151 | freezeDecel: false,
2152 | activeCursor: 'pointer',
2153 | pulsateTo: 1,
2154 | pulsateTime: 3,
2155 | reverse: false,
2156 | depth: 0.5,
2157 | maxSpeed: 0.05,
2158 | minSpeed: 0,
2159 | decel: 0.95,
2160 | interval: 20,
2161 | minBrightness: 0.1,
2162 | maxBrightness: 1,
2163 | outlineColour: '',
2164 | outlineThickness: 2,
2165 | outlineOffset: 5,
2166 | outlineMethod: 'outline',
2167 | outlineRadius: 0,
2168 | textColour: ['#222', '#000'],
2169 | textHeight: 15,
2170 | textFont: 'Helvetica, Arial, sans-serif',
2171 | shadow: '#111',
2172 | shadowBlur: 1,
2173 | shadowOffset: [0.1,0.1],
2174 | initial: null,
2175 | hideTags: false,
2176 | zoom: 0,
2177 | weight: false,
2178 | weightMode: 'size',
2179 | weightFrom: null,
2180 | weightSize: 1,
2181 | weightSizeMin: null,
2182 | weightSizeMax: null,
2183 | weightGradient: {0:'#f00', 0.33:'#ff0', 0.66:'#0f0', 1:'#00f'},
2184 | txtOpt: true,
2185 | txtScale: 2,
2186 | frontSelect: false,
2187 | wheelZoom: true,
2188 | zoomMin: 0.8,
2189 | zoomMax: 0.8,
2190 | zoomStep: 0.05,
2191 | shape: 'sphere',
2192 | lock: null,
2193 | tooltip: null,
2194 | tooltipDelay: 300,
2195 | tooltipClass: 'tctooltip',
2196 | radiusX: 1,
2197 | radiusY: 1,
2198 | radiusZ: 1,
2199 | stretchX: 1,
2200 | stretchY: 1,
2201 | offsetX: 0,
2202 | offsetY: 0,
2203 | shuffleTags: false,
2204 | noSelect: false,
2205 | noMouse: false,
2206 | imageScale: 1,
2207 | paused: false,
2208 | dragControl: false,
2209 | dragThreshold: 4,
2210 | centreFunc: Nop,
2211 | splitWidth: 0,
2212 | animTiming: 'Smooth',
2213 | clickToFront: false,
2214 | fadeIn: 0,
2215 | padding: 0,
2216 | bgColour: null,
2217 | bgRadius: 0,
2218 | bgOutline: null,
2219 | bgOutlineThickness: 0,
2220 | outlineIncrease: 4,
2221 | textAlign: 'centre',
2222 | textVAlign: 'middle',
2223 | imageMode: null,
2224 | imagePosition: null,
2225 | imagePadding: 2,
2226 | imageAlign: 'centre',
2227 | imageVAlign: 'middle',
2228 | noTagsMessage: true,
2229 | centreImage: null,
2230 | pinchZoom: false,
2231 | repeatTags: 0,
2232 | minTags: 0,
2233 | imageRadius: 0,
2234 | scrollPause: false,
2235 | outlineDash: 0,
2236 | outlineDashSpace: 0,
2237 | outlineDashSpeed: 1
2238 | };
2239 | for(i in TagCanvas.options) TagCanvas[i] = TagCanvas.options[i];
2240 | window.TagCanvas = TagCanvas;
2241 | // set a flag for when the window has loaded
2242 | AddHandler('load',function(){TagCanvas.loaded=1},window);
2243 | })();
2244 |
--------------------------------------------------------------------------------