├── 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 | ![GitHub release (latest SemVer including pre-releases)](https://img.shields.io/github/v/release/D0n9x1n/hexo-tag-cloud?include_prereleases) 4 | [![Build Status](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/badges/build.png?b=master)](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/build-status/master) 5 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/badges/quality-score.png?b=master)](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 | ![TagCloud](./img/example.png) 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 |
31 |

<%= __('tagcloud') %>

32 |
33 | 34 | <%- tagcloud() %> 35 | 36 |
37 |
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 |
50 |

Tag Cloud

51 |
52 | 53 | {{ list_tags() }} 54 | 55 |
56 |
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 | ![GitHub release (latest SemVer including pre-releases)](https://img.shields.io/github/v/release/D0n9x1n/hexo-tag-cloud?include_prereleases) 4 | [![Build Status](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/badges/build.png?b=master)](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/build-status/master) 5 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/MikeCoder/hexo-tag-cloud/badges/quality-score.png?b=master)](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 | ![TagCloud](./img/example.png) 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 |
31 |

<%= __('tagcloud') %>

32 |
33 | 34 | <%- tagcloud() %> 35 | 36 |
37 |
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 |
51 |

Tag Cloud

52 |
53 | 54 | {{ list_tags() }} 55 | 56 |
57 |
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 | --------------------------------------------------------------------------------