├── .gitignore ├── README.md ├── _config.yml ├── architecture.html ├── bower.json ├── css └── cornerstoneDemo.css ├── gruntfile.js ├── index.html ├── js ├── about.js ├── cornerstoneDemo.js ├── disableAllTools.js ├── displayThumbnail.js ├── forEachViewport.js ├── help.js ├── imageViewer.js ├── loadStudy.js ├── loadTemplate.js ├── setupButtons.js ├── setupViewport.js └── setupViewportOverlays.js ├── lib ├── bootstrap.min.css ├── bootstrap.min.js ├── cornerstone.css ├── cornerstone.js ├── cornerstone.min.css ├── cornerstoneFileImageLoader.js ├── cornerstoneMath.js ├── cornerstoneTools.js ├── cornerstoneWADOImageLoader.js ├── cornerstoneWebImageLoader.js ├── dicomParser.js ├── hammer.min.js ├── jpx.js ├── jquery-ui.min.css ├── jquery-ui.min.js ├── jquery.js ├── jquery.min.js └── jquery.min.map ├── package.json ├── studies ├── crstudy.json ├── ctstudy.json ├── dermatology.json ├── dxstudy.json ├── mgstudy.json ├── mrstudy.json ├── ptctstudy.json ├── usdopplerstudy.json └── usjpeg.json ├── studyList.json ├── studyViewer.html └── templates ├── about.html ├── help.html ├── studyViewer.html └── viewport.html /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Interactive Medical Images 2 | ================ 3 | 4 | 何使用各种基础组件来构建一个完整的应用程序 5 | 6 | 当前显示一个研究列表,并在选定的系列缩略图和各种工具中显示它们。 7 | 基础模块的使用: 8 | 9 | - [Cornerstone (core)](https://github.com/chafey/cornerstone) 10 | - [Cornerstone Tools](https://github.com/chafey/cornerstoneTools) 11 | - [Cornerstone Math](https://github.com/chafey/cornerstoneMath) 12 | - [Cornerstone WADO image loader](https://github.com/chafey/cornerstoneWADOImageLoader) 13 | - [Cornerstone Web image loader](https://github.com/chafey/cornerstoneWebImageLoader) 14 | 15 | 16 | 安装 17 | ================ 18 | 19 | - 克隆 这个应用程序 并切换到它的目录 20 | 21 | - 安装依赖 [Bower](http://bower.io/): 22 | 23 | > 运行 24 | 25 | 使用方法: 26 | ================ 27 | * 这个程序需要 http 开头的路径才能正常运行,所以需要搭建一个运行环境 28 | 1. 安装 XAMPP 克隆这个项目放到 XAMPP 安装目录下的 htdcos 目录下 启动 Apache Web服务器 29 | 30 | * 了解这个程序的运行原理 31 | 1. 在浏览器中输入 http://127.0.0.1/InteractiveMedicalImages/architecture.html 查看程序运行相关依赖 和 原理 32 | 33 | * 下面开始运行这个程序 34 | 1. 在浏览器中输入 http://127.0.0.1/InteractiveMedicalImages/ [此时会看到一个黑色的界面] 35 | 2. 打控制台看是否有报错 ( 有的话 运行环境的问题 调整运行环境) 36 | 37 | * 将这个程序融入到 自己的项目中 38 | 1. index.html : 这是项目的整体UI界面 其他 UI 都是通过动态插入html模板进行加载的, 在文最底部 是 入口 js 看懂了这段 js 代码 项目基本已经 融入50%了 39 | 2. disableAllTools.js 和 displayThumbnail.js 是显示隐藏工具的 其他的文件自己看文件名就能懂了 40 | 3. 重点在 loadStudy.js 这个是负责加载数据的 搜索 dicomweb://10.0.0.5/testDICOM/ 将他修改成 你自己的文地址 比如: dicomweb://127.0.0.1/testDICOM/ 就ok了 注意这里有坑 dicomweb 不可以修改这个前缀可以自动识别 协议名 http ,https ...千万不能改 41 | 4. 注意看 studies 文件夹 json 数据是与 loadstudy.js 中的 ImageId 像对应的 所以需要配置对应路径的文件夹以及文件 都准备好了 [点击下载](https://github.com/bianliuzhu/testDicom) 将这个文件加压后 修改文件名为 testDICOM 并把这个文件夹 copy 到 htdocs 目录下 42 | 5. 现在项目可以跑起来了 43 | 6. 如有错误请指正 [Email:bianliuzhu@gmail.com] 44 | 45 | 46 | 疑问: 47 | ================ 48 | 49 | 2018年9月5日 网友 [葉子紛飛](75797351@qq.com) 给我发来邮件,遇到两个问题: 50 | 51 | 问题描述: 52 | 53 | 1.部分标准影像无法展示 54 | [葉子紛飛](75797351@qq.com)已经提供解决方案: 55 | 只需要修改原来项目的InteractiveMedicalImages\lib\dicomParser.js文件即可, 56 | 57 | 可以从[DicomParsing](https://github.com/GleasonBian/DicomParsing)项目内提取到dicomParser.js文件。 58 | 59 | 2.非标准Dicom影像无法显示 60 | 目前该问题 尚未 解决 61 | 62 | 感谢: 63 | ================ 64 | 非常感谢 @[葉子紛飛](75797351@qq.com) 的 提醒 邮件地址已更正 -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /architecture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

13 | Architecture 14 |

15 |

16 | This page describes the architecture of the Cornerstone Demo application 17 |

18 |
19 |
20 |
Module Dependency Graph
21 |
22 |
23 |
zZjdU6MwEMD/mj7a4asfPGq13s3cjc54N949ppCW3AFxQqqtf70LbAIB/ChahT6UbJLdsL/dZGHkLpLdpSB30U8e0njkWOFu5J6PHMf2pz785ZJ9KZlbVinYCBbiIBRsWUgzQyQ5jyW7M4UBT1MaSEO25rGp7I5saEtwE5C4Lb1loYxQaqvl5h3fKNtEaGfuTMuOTO6VjpCuyTaWJ4UI+vLuhChd+Jg7q2ye2LaHRvcocrSt1FjUI+eJIRA0Y4/mwtcMF4ZmVlyEVBiimKX/655zL4CT4Bwm5nfJbkHjnJXCUE5bPtOrHSZoaph+doJbzrgn8RbX3vIgEYI/jNwzUCn2f0Bmq8ZfaFjjSdsoriPjWxGgFgctSSI2FIepcTQ04gCXekl5QsEKDHio0VehGdXAa6GgMZHs3sRA0MEbrVDbuOYM1gwBgPwnShEmwmSKy1Y6ykfCaXXHtjRNTE1eU1PpiJYmuKk9eiUqwD0D0Xs7xLexQqQGK1z+cFj5DQ+72DwYldeArttHQIWePS6qwaWVbdmNvOrLavaJrPAwOS4r1DmgtFJHE3rYnvdlZVuzBiwVBUeANetxkMERbx5k0Nwxqc84uD/oiEOTQ89FtVHqfbPvGec3NDlNTb35/s6ouFr9yytIqJHICurVQuGCi5SKTPKUVvVT2RVJmdegp7kVZ7lhMtquxgEUac4yiMiagk+WQXO6Ch+DCoZWLXLWPJULHvOydnMtuC5g7Fkuv8Fh3kvbwD0VkqpyuzsAlFPnU4w25VTXHecG1YUaX4sXr5Fq9cgwOFROh2bN72/nYP2CN4DsnTxqOjSUIp8GAQXeCxqh/joVNeNLqNyenl99T+D96Acn+TvH++B0atOYiqQfBKbpwZR01fhVuXNOE/5OOpUKjaTYQoaxnc3N7Uz5+7VMaVaIH8sgZODOayKgu4/zW9O144ua8YMc3/JyB4sXc6Er9hWGaWfv52RGq3pzDqjeOuupfp8f6rC+ohSbzfyxX7/MzWviGL3z/p8iJmPfqn7obP0GBb0vmOldw0Gz+pBVDq++ProXTw==
24 |
25 |
26 |
27 |
28 |
Communication Diagram
29 |
30 |
31 |
1Vpbb+K6E/80PIKSONweC5TtHrU6lai05/9UGWLA/03iyDGl7KffcTIOuXUXSppSHiAe4/HMb642dMg0eP0mabR9EB7zO47lvXbIrOM4I8uCd004lAgbyb2UZCNhxz0WF0hKCF/xqEhciTBkK1WgrYVfZBbRDasQFivqV6k/uKe2KJwzONLvGN9szTb2YJzOxOpgeHhsTXe+6iYkmNPTATW8UM1XKx3i8gMOzTiiYUGgX0IEBYJkMf9VFHrNUSrcYimkx2SB5PPwZx41cgsmkkLAQv0UvE6Zr81kTJAum78xm4ElWVjY+s0Fw3TFC/V3KHsFvf2WK7aI6EqP9+A7HTLZSOpx2GMqfJEqRGbWzJ33kR2TiqFj1ch01BSckYmAKXnQFkhnXcdFE6RjgiLtj+Z3jW9uc6bv20ikCOgmY31EAB4QhHpACHkPIJcrnQmPSrsWQpnT2jbfyWvtNqE06ti60saMxtLjNpVGcdtW2h2XlB61qLSNafOa4r3k+QSHOTiIoTUf7n+HI97SSD/uAv9mpbT2E60uhxJ1T5fMfxQxV1zo6rAUSumqkH3hxucbPaFEE54zKAI1xMqUB6oGJ7PqEpiMmU+AiQdJQX8fRmSSLL+Jo7RvgBqcUuB5q5TuL2600M585YVOj0OHseYh1NTeCng6c48qCh+aHsNnlDx0WRwDwpz6mtaHijffsyUIxLq2M+pF4aYJJx5jJc2iuhrUDtLy1smIl5jnhKBmHnRVOAxFyNIvaOqfFQYmYieTVHAMF0XlhpkwrIclr3ddsc6IkvlU8ZeiGHVg4B6PgoOAR9xH5WxacvdUfFyV74RKjKDfLTDKxoZRqnSFUWKbTPGTzFXtuaZChkzGKjGMNWOBqMbXngc+hXky+YiqZJt+w6hfk4adOkNmxIvKEvLIQXL39HBfAQH0g20nFFPGCpTT/XQllwTc8/SaSayk+MlyVctKXicBlpjpD11qqV+rxnsdXI0U8WrnMl0svhBYpgNqBaxqb/sPfaHxSvIoDeSvgZndpodhZ3Fd9d7+e72XYg3FHjamfldfSSyp7KY9gNZ9AOfK+Q+2fI6ZBGmeIZMzyLvq+QV2F7KhTmBYPtTUdQJ4wi12AgjyRd6OjM9qBciEvXL1HwxtfP6ftkUPK8IpTUK+I0grx+e1BKRUylwLQTm3Jeib3JExIgaT5psCG90kn9R9feS6PEetRaiMwbWFk3F6V5Xt+rEHuI/L7tVMtUiC+/pRcw1OnwCbuX3MwRarnXfo/T+GpP2pRfHMy5NWUTvhxuijk2tquetJrsTF08y5yXVgYRC9xai51GrMVPb2ex6rL+fxzgBxasXjT7gf+3CPT7a8Ho93Ru9sJ8oeX2HUoMdX28C7p6dHoHy7fbrc2xtw6yxxGzDaPN6ccp/ZzIUZbpT35zdOfa1dmJWOJ7Y96rmk747IaGiNB0Pz8+nZ92dW8QLUtt2ea40GlmP3rT4Z4h1l875uHClnzNn36b8PQPquj5JwMGw2u2OSq/lJZJq82vqNqKHogOHx9+YU/+P/A8jtbw==
32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cornerstoneDemo", 3 | "version": "0.1.0", 4 | "description": "Cornerstone Demo Application", 5 | "main": "", 6 | "ignore": [ 7 | "examples", 8 | "src", 9 | "test", 10 | "gruntfile.js", 11 | "package.json", 12 | "README.md", 13 | ".gitignore" 14 | ], 15 | "dependencies": { 16 | "cornerstone": "~0.8.1", 17 | "cornerstoneTools": "~0.7.2", 18 | "cornerstoneWADOImageLoader": "~0.7.0", 19 | "cornerstoneWebImageLoader": "~0.5.2", 20 | "cornerstoneMath": "~0.1.2", 21 | "image-jpeg2000": "~0.3.1", 22 | "bootstrap": "~3.3.4", 23 | "hammerjs": "~2.0.4", 24 | "jquery" : "~2.1.1" 25 | }, 26 | "devDependencies": { 27 | "qunit": "~1.14.0" 28 | }, 29 | "license": "MIT", 30 | "authors": [ 31 | "Chris Hafey" 32 | ], 33 | "homepage": "https://github.com/chafey/cornerstoneDemo", 34 | "keywords": [ 35 | "DICOM", 36 | "medical", 37 | "imaging", 38 | "WADO", 39 | "cornerstone" 40 | ], 41 | "repository": { 42 | "type": "git", 43 | "url": "https://github.com/chafey/cornerstoneDemo.git" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /css/cornerstoneDemo.css: -------------------------------------------------------------------------------- 1 | /* prevent 'bounce' in scrolling */ 2 | html { 3 | height: 100%; 4 | width: 100%; 5 | overflow: hidden; 6 | } 7 | 8 | body { 9 | height: 100%; 10 | width: 100%; 11 | overflow: auto; 12 | color: #888888; 13 | background-color: #060606; 14 | } 15 | 16 | .hidden { 17 | display: none; 18 | } 19 | 20 | .modal-content { 21 | background-color: #202020; 22 | } 23 | 24 | .btn-default { 25 | color: #ffffff; 26 | background-color: #424242; 27 | border-color: #424242; 28 | } 29 | 30 | /* images are displayed in viewports */ 31 | .viewport { 32 | width:100%; 33 | height:100%; 34 | top:0px; 35 | left:0px; 36 | position:absolute 37 | } 38 | 39 | .overlay { 40 | position: absolute; 41 | color: #e4ad00; 42 | } 43 | 44 | .imageViewer { 45 | } 46 | 47 | .myNav { 48 | margin: 0; 49 | border:0; 50 | } 51 | 52 | .viewportWrapper { 53 | box-sizing: border-box; 54 | border: 2px solid #777; 55 | } 56 | .renderTime {} 57 | .fps {} 58 | 59 | .csthumbnail { 60 | color: white; 61 | background-color:black; 62 | width:100px; 63 | height:100px; 64 | border: 0px; 65 | padding: 0px; 66 | } 67 | 68 | .viewer { 69 | /*position: absolute;left: 110px;width: 100%;*/ 70 | float: left; 71 | box-sizing: border-box; 72 | } 73 | 74 | .thumbnailSelector { 75 | width:106px; 76 | float:left; 77 | margin-left:0px; 78 | height: 100%; 79 | } 80 | body, html {height:100%} 81 | 82 | #wrap {height:100%} 83 | 84 | .studyContainer { 85 | margin-top: 2px; 86 | margin-left:0px; 87 | margin-right:0px; 88 | padding: 0px; 89 | } 90 | 91 | .container { 92 | } 93 | 94 | .navbar-default { 95 | background-color: #060606; 96 | border-color: #000000; 97 | } 98 | 99 | .thumbnails { 100 | margin:0px; 101 | margin-bottom: 0px; 102 | overflow-y:scroll; 103 | overflow-x:hidden; 104 | } 105 | 106 | .table { 107 | border-collapse: collapse; 108 | border-color: gray; 109 | } 110 | 111 | .table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { 112 | padding: 4px; 113 | border: 1px solid #282828; 114 | } 115 | 116 | .table-striped>tbody>tr:nth-of-type(odd) { 117 | background-color: #202020; 118 | } 119 | 120 | .studyRow { 121 | margin-left: 0px; 122 | margin-rigth: 0px; 123 | height:100%; 124 | } 125 | .row { 126 | margin:0; 127 | } 128 | 129 | a.list-group-item { 130 | background-color: black; 131 | padding: 2px; 132 | border: 1px solid #424242; 133 | z-index: 5; 134 | } 135 | a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:focus { 136 | background-color: #424242; 137 | border-color: #4e4e4e; 138 | } 139 | 140 | .nav-tabs { 141 | border-bottom: 1px solid #424242; 142 | } 143 | 144 | .nav-tabs>li>a { 145 | background-color: #202020; 146 | border-color: #424242; 147 | color: #424242; 148 | padding: 3px 10px; 149 | } 150 | 151 | .nav-tabs>li.active>a, .nav-tabs>li.active>a:hover, .nav-tabs>li.active>a:focus { 152 | background-color: #424242; 153 | border-color: #424242; 154 | color: #ffffff; 155 | padding: 3px 10px; 156 | } 157 | 158 | .dropdown-menu { 159 | min-width: 40px; 160 | } -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | clean: { 7 | default: { 8 | src: [ 9 | 'dist', 10 | 'docs', 11 | 'build' 12 | ] 13 | } 14 | }, 15 | copy: { 16 | bower: { 17 | src: [ 18 | 'bower_components/cornerstone/dist/cornerstone.min.css', 19 | 'bower_components/cornerstone/dist/cornerstone.js', 20 | 'bower_components/cornerstoneTools/dist/cornerstoneTools.js', 21 | 'bower_components/cornerstoneWADOImageLoader/dist/cornerstoneWADOImageLoader.js', 22 | 'bower_components/cornerstoneWebImageLoader/dist/cornerstoneWebImageLoader.js', 23 | 'bower_components/cornerstoneMath/dist/cornerstoneMath.js', 24 | 'bower_components/cornerstone-file-image-loader/dist/cornerstoneFileImageLoader.js', 25 | 'bower_components/image-jpeg2000/dist/jpx.js', 26 | 'bower_components/dicomParser/dist/dicomParser.js', 27 | 'bower_components/bootstrap/dist/js/bootstrap.min.js', 28 | 'bower_components/hammerjs/hammer.min.js', 29 | 'bower_components/jquery/dist/jquery.js', 30 | 'bower_components/jquery/dist/jquery.min.js', 31 | 'bower_components/jquery/dist/jquery.min.map', 32 | 'bower_components/bootstrap/dist/css/bootstrap.min.css' 33 | ], 34 | dest: 'lib', 35 | expand: true, 36 | flatten: true 37 | } 38 | 39 | } 40 | }); 41 | 42 | require('load-grunt-tasks')(grunt); 43 | 44 | grunt.registerTask('buildAll', ['clean']); 45 | grunt.registerTask('default', ['buildAll']); 46 | }; 47 | 48 | //发布流程: 49 | 50 | // 1)更新版本号 51 | 52 | // 2)进行构建(需要用正确的构建号更新分区版本) 53 | 54 | // 3)提交修改 55 | 56 | // git提交是“改变....” 57 | 58 | // 4)标记提交 59 | 60 | // git标签- 0.1.0 -m“版本0.1.0” 61 | 62 | // 5)推到github 63 | 64 | // git推源主标签 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 45 | 46 |
47 | 48 | 53 | 54 | 55 |
56 | 57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
患者姓名患者idStudy 日期状态Study 描述# 图像
74 |
75 |
76 |
77 | 78 | 79 |
80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /js/about.js: -------------------------------------------------------------------------------- 1 | loadTemplate("templates/about.html", function(element) { 2 | $('body').append(element); 3 | $("#about").click(function() { 4 | $("#aboutModal").modal(); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /js/cornerstoneDemo.js: -------------------------------------------------------------------------------- 1 | // 负载在HTML模板 2 | 3 | var viewportTemplate; // 视图模板 4 | loadTemplate("templates/viewport.html", function (element) { 5 | console.log(element); 6 | viewportTemplate = element; 7 | }); 8 | 9 | var studyViewerTemplate; // the study 视图模板 10 | loadTemplate("templates/studyViewer.html", function (element) { 11 | studyViewerTemplate = element; 12 | }); 13 | 14 | // 从JSON清单中获取study列表 15 | $.getJSON('studyList.json', function (data) { 16 | data.studyList.forEach(function (study) { 17 | 18 | // 为清单中的每个 study 研究创建一个表行 19 | var studyRow = '' + 20 | study.patientName + '' + 21 | study.patientId + '' + 22 | study.studyDate + '' + 23 | study.modality + '' + 24 | study.studyDescription + '' + 25 | study.numImages + '' + 26 | ''; 27 | 28 | // 将这一行附加到学习列表中 29 | var studyRowElement = $(studyRow).appendTo('#studyListData'); 30 | 31 | // 在学习列表中点击。 32 | $(studyRowElement).click(function () { 33 | 34 | //为studay添加新的标签并切换到它 35 | var studyTab = '
  • ' + study.patientName + '
  • '; 36 | $('#tabs').append(studyTab); 37 | 38 | //通过复制studyViewerTemplate元素添加选项卡内容 39 | var studyViewerCopy = studyViewerTemplate.clone(); 40 | 41 | /*var viewportCopy = viewportTemplate.clone(); 42 | studyViewerCopy.find('.imageViewer').append(viewportCopy);*/ 43 | 44 | 45 | studyViewerCopy.attr("id", 'x' + study.patientId); 46 | // 构建 可见视图 47 | studyViewerCopy.removeClass('hidden'); 48 | // 向选项卡内容添加部分 49 | studyViewerCopy.appendTo('#tabContent'); 50 | 51 | // 显示新的选项卡(这将是刚刚添加的最后一个选项卡 52 | $('#tabs a:last').tab('show'); 53 | 54 | // 切换窗口大小(?) 55 | $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { 56 | $(window).trigger('resize'); 57 | }); 58 | 59 | //现在加载study.json 60 | loadStudy(studyViewerCopy, viewportTemplate, study.studyId + ".json"); 61 | }); 62 | }); 63 | }); 64 | 65 | 66 | // 显示选项卡上单击 67 | $('#tabs a').click(function (e) { 68 | e.preventDefault(); 69 | $(this).tab('show'); 70 | }); 71 | 72 | // 主要调整 73 | function resizeMain() { 74 | var height = $(window).height(); 75 | $('#main').height(height - 50); 76 | $('#tabContent').height(height - 50 - 42); 77 | } 78 | 79 | 80 | // 调用窗口调整大小 81 | $(window).resize(function () { 82 | resizeMain(); 83 | }); 84 | resizeMain(); 85 | 86 | 87 | // 防止滚动iOS 88 | document.body.addEventListener('touchmove', function (e) { 89 | e.preventDefault(); 90 | }); -------------------------------------------------------------------------------- /js/disableAllTools.js: -------------------------------------------------------------------------------- 1 | // Disable all tools 2 | function disableAllTools() { 3 | forEachViewport(function(element) { 4 | cornerstoneTools.wwwc.disable(element); 5 | cornerstoneTools.pan.activate(element, 2); // 2 is middle mouse button 6 | cornerstoneTools.zoom.activate(element, 4); // 4 is right mouse button 7 | cornerstoneTools.probe.deactivate(element, 1); 8 | cornerstoneTools.length.deactivate(element, 1); 9 | cornerstoneTools.angle.deactivate(element, 1); 10 | cornerstoneTools.ellipticalRoi.deactivate(element, 1); 11 | cornerstoneTools.rectangleRoi.deactivate(element, 1); 12 | cornerstoneTools.stackScroll.deactivate(element, 1); 13 | cornerstoneTools.wwwcTouchDrag.deactivate(element); 14 | cornerstoneTools.zoomTouchDrag.deactivate(element); 15 | cornerstoneTools.panTouchDrag.deactivate(element); 16 | cornerstoneTools.stackScrollTouchDrag.deactivate(element); 17 | }); 18 | } -------------------------------------------------------------------------------- /js/displayThumbnail.js: -------------------------------------------------------------------------------- 1 | function displayThumbnail(seriesList, seriesElement, element, stack, loaded) { 2 | // Deactivate other thumbnails 3 | $(seriesList).find('a').each(function() { 4 | $(this).removeClass('active'); 5 | }); 6 | 7 | // Make this series visible 8 | 9 | // Make the selected thumbnail active 10 | $(seriesElement).addClass('active'); 11 | 12 | var enabledImage = cornerstone.getEnabledElement(element); 13 | if (enabledImage.image) { 14 | // Stop clip from if playing on element 15 | cornerstoneTools.stopClip(element); 16 | // Disable stack scrolling 17 | cornerstoneTools.stackScroll.disable(element); 18 | // Enable stackScroll on selected series 19 | cornerstoneTools.stackScroll.enable(element); 20 | } 21 | 22 | // Load the first image of the selected series stack 23 | cornerstone.loadAndCacheImage(stack.imageIds[0]).then(function(image) { 24 | if (loaded) { 25 | loaded.call(image, element, stack); 26 | } 27 | 28 | // Get the state of the stack tool 29 | var stackState = cornerstoneTools.getToolState(element, 'stack'); 30 | stackState.data[0] = stack; 31 | stackState.data[0].currentImageIdIndex = 0; 32 | 33 | // Get the default viewport 34 | var defViewport = cornerstone.getDefaultViewport(element, image); 35 | // Get the current series stack index 36 | // Display the image 37 | cornerstone.displayImage(element, image, defViewport); 38 | // Fit the image to the viewport window 39 | cornerstone.fitToWindow(element); 40 | 41 | // Prefetch the remaining images in the stack (?) 42 | cornerstoneTools.stackPrefetch.enable(element); 43 | 44 | // Play clip if stack is a movie (has framerate) 45 | if (stack.frameRate !== undefined) { 46 | cornerstoneTools.playClip(element, stack.frameRate); 47 | } 48 | }); 49 | }; -------------------------------------------------------------------------------- /js/forEachViewport.js: -------------------------------------------------------------------------------- 1 | function forEachViewport(callback) { 2 | var elements = $('.viewport'); 3 | $.each(elements, function(index, value) { 4 | var element = value; 5 | try { 6 | callback(element); 7 | } 8 | catch(e) { 9 | 10 | } 11 | }); 12 | } -------------------------------------------------------------------------------- /js/help.js: -------------------------------------------------------------------------------- 1 | loadTemplate("templates/help.html", function(element) { 2 | $('body').append(element); 3 | $("#help").click(function() { 4 | $("#helpModal").modal(); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /js/imageViewer.js: -------------------------------------------------------------------------------- 1 | ImageViewer = function(root, viewport) { 2 | var self = this; 3 | 4 | self.root = root; 5 | self.stacks = []; 6 | self.viewports = []; 7 | self.layout = '1x1'; 8 | self.viewportModel = viewport; 9 | 10 | self.setLayout = function(layout) { 11 | self.layout = layout; 12 | // TODO: create viewports 13 | var ab = self.getRowsCols(), a = ab[0], b = ab[1], numOfViewports = a * b, 14 | perWidth = 100 / b, perHeight = 100 / a; 15 | self.root.find('.imageViewer').html(''); 16 | var i = 0; 17 | self.viewports = []; 18 | while (i < numOfViewports) { 19 | var elem = self.viewportModel.clone().css({ 20 | width : perWidth + '%', height : perHeight + '%' 21 | }).appendTo(self.root.find('.imageViewer')); 22 | elem.find('.viewport').data('index', i).data('waiting', true); 23 | 24 | self.viewports.push(elem); 25 | i++; 26 | } 27 | } 28 | 29 | self.getRowsCols = function() { 30 | var s = self.layout.split(/x/); 31 | return [parseInt(s[0]), parseInt(s[1])]; 32 | } 33 | 34 | self.isSingle = function() { 35 | return self.layout == '1x1'; 36 | } 37 | 38 | self.getElement = function(item) { 39 | return self.viewports[item].find('.viewport')[0]; 40 | } 41 | 42 | self.forEachElement = function(cb) { 43 | self.viewports.forEach(function(vp, i){ 44 | cb.call(self, vp.find('.viewport')[0], vp, i); 45 | }); 46 | } 47 | } -------------------------------------------------------------------------------- /js/loadStudy.js: -------------------------------------------------------------------------------- 1 | // 为 研究 对象 加载 研究信息 2 | function loadStudy(studyViewer, viewportModel, studyId) { 3 | console.log(studyViewer); 4 | // 获取选定的 studyId 的JSON数据 5 | $.getJSON('studies/' + studyId, function (data) { 6 | var imageViewer = new ImageViewer(studyViewer, viewportModel); 7 | imageViewer.setLayout('1x1'); // default layout 8 | function initViewports() { 9 | imageViewer.forEachElement(function (el) { 10 | cornerstone.enable(el); 11 | $(el).droppable({ 12 | drop: function (evt, ui) { 13 | var fromStack = $(ui.draggable.context).data('stack'), 14 | toItem = $(this).data('index'); 15 | useItemStack(toItem, fromStack); 16 | } 17 | }); 18 | }); 19 | } 20 | 21 | // 设置工具按钮 22 | setupButtons(studyViewer); 23 | 24 | //布局选择 25 | $(studyViewer).find('.choose-layout a').click(function () { 26 | var previousUsed = []; 27 | imageViewer.forEachElement(function (el, vp, i) { 28 | if (!isNaN($(el).data('useStack'))) { 29 | previousUsed.push($(el).data('useStack')); 30 | } 31 | }); 32 | 33 | var type = $(this).text(); 34 | imageViewer.setLayout(type); 35 | initViewports(); 36 | resizeStudyViewer(); 37 | if (previousUsed.length > 0) { 38 | previousUsed = previousUsed.slice(0, imageViewer.viewports.length); 39 | var item = 0; 40 | previousUsed.forEach(function (v) { 41 | useItemStack(item++, v); 42 | }); 43 | } 44 | 45 | //return false; 46 | }); 47 | 48 | // 将第一个系列加载到viewport (?) 49 | //var stacks = []; 50 | //var currentStackIndex = 0; 51 | var seriesIndex = 0; 52 | 53 | //为每个系列创建一个堆栈对象 54 | data.seriesList.forEach(function (series) { 55 | var stack = { 56 | seriesDescription: series.seriesDescription, 57 | stackId: series.seriesNumber, 58 | imageIds: [], 59 | seriesIndex: seriesIndex, 60 | currentImageIdIndex: 0, 61 | frameRate: series.frameRate 62 | }; 63 | //console.log(stack); 64 | 65 | // 用每个系列的 imageIds 填充 imageIds 数组 66 | // 对于具有帧信息的系列,通过请求每个帧获取图像url 67 | if (series.numberOfFrames !== undefined) { 68 | var numberOfFrames = series.numberOfFrames; 69 | for (var i = 0; i < numberOfFrames; i++) { 70 | var imageId = series.instanceList[0].imageId + "?frame=" + i; 71 | if (imageId.substr(0, 4) !== 'http') { 72 | imageId = "dicomweb://10.0.0.5/testDICOM/" + imageId; //"dicomweb://127.0.0.1/testDICOM/" + imageId; 73 | } 74 | stack.imageIds.push(imageId); 75 | } 76 | // Otherwise, get each instance url 77 | } else { 78 | series.instanceList.forEach(function (image) { 79 | var imageId = image.imageId; 80 | if (image.imageId.substr(0, 4) !== 'http') { 81 | imageId = "dicomweb://10.0.0.5/testDICOM/" + imageId; 82 | } 83 | stack.imageIds.push(imageId); 84 | }); 85 | } 86 | // Move to next series 87 | seriesIndex++; 88 | 89 | // Add the series stack to the stacks array 90 | imageViewer.stacks.push(stack); 91 | }); 92 | 93 | // Resize the parent div of the viewport to fit the screen 94 | var imageViewerElement = $(studyViewer).find('.imageViewer')[0]; 95 | var viewportWrapper = $(imageViewerElement).find('.viewportWrapper')[0]; 96 | var parentDiv = $(studyViewer).find('.viewer')[0]; 97 | 98 | //viewportWrapper.style.width = (parentDiv.style.width - 10) + "px"; 99 | //viewportWrapper.style.height = (window.innerHeight - 150) + "px"; 100 | 101 | var studyRow = $(studyViewer).find('.studyRow')[0]; 102 | var width = $(studyRow).width(); 103 | 104 | //$(parentDiv).width(width - 170); 105 | //viewportWrapper.style.width = (parentDiv.style.width - 10) + "px"; 106 | //viewportWrapper.style.height = (window.innerHeight - 150) + "px"; 107 | 108 | // Get the viewport elements 109 | var element = $(studyViewer).find('.viewport')[0]; 110 | 111 | // Image enable the dicomImage element 112 | initViewports(); 113 | //cornerstone.enable(element); 114 | 115 | // Get series list from the series thumbnails (?) 116 | var seriesList = $(studyViewer).find('.thumbnails')[0]; 117 | imageViewer.stacks.forEach(function (stack, stackIndex) { 118 | 119 | // Create series thumbnail item 120 | var seriesEntry = '' + 125 | '
    ' + 130 | "
    " + stack.seriesDescription + '
    '; 131 | 132 | // Add to series list 133 | var seriesElement = $(seriesEntry).appendTo(seriesList); 134 | 135 | // Find thumbnail 136 | var thumbnail = $(seriesElement).find('div')[0]; 137 | 138 | // Enable cornerstone on the thumbnail 139 | cornerstone.enable(thumbnail); 140 | 141 | // Have cornerstone load the thumbnail image 142 | cornerstone.loadAndCacheImage(imageViewer.stacks[stack.seriesIndex].imageIds[0]).then(function (image) { 143 | // Make the first thumbnail active 144 | if (stack.seriesIndex === 0) { 145 | $(seriesElement).addClass('active'); 146 | } 147 | // Display the image 148 | cornerstone.displayImage(thumbnail, image); 149 | $(seriesElement).draggable({ 150 | helper: "clone" 151 | }); 152 | }); 153 | 154 | // Handle thumbnail click 155 | $(seriesElement).on('click touchstart', function () { 156 | useItemStack(0, stackIndex); 157 | }).data('stack', stackIndex); 158 | }); 159 | 160 | function useItemStack(item, stack) { 161 | var imageId = imageViewer.stacks[stack].imageIds[0], 162 | element = imageViewer.getElement(item); 163 | if ($(element).data('waiting')) { 164 | imageViewer.viewports[item].find('.overlay-text').remove(); 165 | $(element).data('waiting', false); 166 | } 167 | $(element).data('useStack', stack); 168 | 169 | displayThumbnail(seriesList, $(seriesList).find('.list-group-item')[stack], element, imageViewer.stacks[stack], function (el, stack) { 170 | if (!$(el).data('setup')) { 171 | setupViewport(el, stack, this); 172 | setupViewportOverlays(el, data); 173 | $(el).data('setup', true); 174 | } 175 | }); 176 | /*cornerstone.loadAndCacheImage(imageId).then(function(image){ 177 | setupViewport(element, imageViewer.stacks[stack], image); 178 | setupViewportOverlays(element, data); 179 | });*/ 180 | } 181 | // Resize study viewer 182 | function resizeStudyViewer() { 183 | var studyRow = $(studyViewer).find('.studyContainer')[0]; 184 | var height = $(studyRow).height(); 185 | var width = $(studyRow).width(); 186 | //console.log($(studyRow).innerWidth(), $(studyRow).outerWidth(), $(studyRow).width()); 187 | $(seriesList).height("100%"); 188 | $(parentDiv).width(width - $(studyViewer).find('.thumbnailSelector:eq(0)').width()); 189 | $(parentDiv).css({ 190 | height: '100%' 191 | }); 192 | $(imageViewerElement).css({ 193 | height: $(parentDiv).height() - $(parentDiv).find('.text-center:eq(0)').height() 194 | }); 195 | 196 | imageViewer.forEachElement(function (el, vp) { 197 | cornerstone.resize(el, true); 198 | 199 | if ($(el).data('waiting')) { 200 | var ol = vp.find('.overlay-text'); 201 | if (ol.length < 1) { 202 | ol = $('
    Please drag a stack onto here to view images.
    ').appendTo(vp); 203 | } 204 | var ow = vp.width() / 2, 205 | oh = vp.height() / 2; 206 | ol.css({ 207 | top: oh, 208 | left: ow - (ol.width() / 2) 209 | }); 210 | } 211 | }); 212 | } 213 | // Call resize viewer on window resize 214 | $(window).resize(function () { 215 | resizeStudyViewer(); 216 | }); 217 | resizeStudyViewer(); 218 | if (imageViewer.isSingle()) 219 | useItemStack(0, 0); 220 | 221 | }); 222 | } -------------------------------------------------------------------------------- /js/loadTemplate.js: -------------------------------------------------------------------------------- 1 | function loadTemplate(url, callback) { 2 | $.get(url, function(data) { 3 | var parsed = $.parseHTML(data); 4 | $.each(parsed, function(index, ele) { 5 | if(ele.nodeName === 'DIV') 6 | { 7 | var element = $(ele); 8 | callback(element); 9 | } 10 | }); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /js/setupButtons.js: -------------------------------------------------------------------------------- 1 | 2 | function setupButtons(studyViewer) { 3 | // Get the button elements 4 | var buttons = $(studyViewer).find('button'); 5 | 6 | // Tool button event handlers that set the new active tool 7 | 8 | // WW/WL 9 | $(buttons[0]).on('click touchstart', function() { 10 | disableAllTools(); 11 | forEachViewport(function(element) { 12 | cornerstoneTools.wwwc.activate(element, 1); 13 | cornerstoneTools.wwwcTouchDrag.activate(element); 14 | }); 15 | }); 16 | 17 | // Invert 18 | $(buttons[1]).on('click touchstart', function() { 19 | disableAllTools(); 20 | forEachViewport(function(element) { 21 | var viewport = cornerstone.getViewport(element); 22 | // Toggle invert 23 | if (viewport.invert === true) { 24 | viewport.invert = false; 25 | } else { 26 | viewport.invert = true; 27 | } 28 | cornerstone.setViewport(element, viewport); 29 | }); 30 | }); 31 | 32 | // Zoom 33 | $(buttons[2]).on('click touchstart', function() { 34 | disableAllTools(); 35 | forEachViewport(function(element) { 36 | cornerstoneTools.zoom.activate(element, 5); // 5 is right mouse button and left mouse button 37 | cornerstoneTools.zoomTouchDrag.activate(element); 38 | }); 39 | }); 40 | 41 | // Pan 42 | $(buttons[3]).on('click touchstart', function() { 43 | disableAllTools(); 44 | forEachViewport(function(element) { 45 | cornerstoneTools.pan.activate(element, 3); // 3 is middle mouse button and left mouse button 46 | cornerstoneTools.panTouchDrag.activate(element); 47 | }); 48 | }); 49 | 50 | // Stack scroll 51 | $(buttons[4]).on('click touchstart', function() { 52 | disableAllTools(); 53 | forEachViewport(function(element) { 54 | cornerstoneTools.stackScroll.activate(element, 1); 55 | cornerstoneTools.stackScrollTouchDrag.activate(element); 56 | }); 57 | }); 58 | 59 | // Length measurement 60 | $(buttons[5]).on('click touchstart', function() { 61 | disableAllTools(); 62 | forEachViewport(function(element) { 63 | cornerstoneTools.length.activate(element, 1); 64 | }); 65 | }); 66 | 67 | // Angle measurement 68 | $(buttons[6]).on('click touchstart', function() { 69 | disableAllTools(); 70 | forEachViewport(function(element) { 71 | cornerstoneTools.angle.activate(element, 1); 72 | }); 73 | }); 74 | 75 | // Pixel probe 76 | $(buttons[7]).on('click touchstart', function() { 77 | disableAllTools(); 78 | forEachViewport(function(element) { 79 | cornerstoneTools.probe.activate(element, 1); 80 | }); 81 | }); 82 | 83 | // Elliptical ROI 84 | $(buttons[8]).on('click touchstart', function() { 85 | disableAllTools(); 86 | forEachViewport(function(element) { 87 | cornerstoneTools.ellipticalRoi.activate(element, 1); 88 | }); 89 | }); 90 | 91 | // Rectangle ROI 92 | $(buttons[9]).on('click touchstart', function() { 93 | disableAllTools(); 94 | forEachViewport(function (element) { 95 | cornerstoneTools.rectangleRoi.activate(element, 1); 96 | }); 97 | }); 98 | 99 | // Play clip 100 | $(buttons[10]).on('click touchstart', function() { 101 | forEachViewport(function(element) { 102 | var stackState = cornerstoneTools.getToolState(element, 'stack'); 103 | var frameRate = stackState.data[0].frameRate; 104 | // Play at a default 10 FPS if the framerate is not specified 105 | if (frameRate === undefined) { 106 | frameRate = 10; 107 | } 108 | cornerstoneTools.playClip(element, frameRate); 109 | }); 110 | }); 111 | 112 | // Stop clip 113 | $(buttons[11]).on('click touchstart', function() { 114 | forEachViewport(function(element) { 115 | cornerstoneTools.stopClip(element); 116 | }); 117 | }); 118 | 119 | // Tooltips 120 | $(buttons[0]).tooltip(); 121 | $(buttons[1]).tooltip(); 122 | $(buttons[2]).tooltip(); 123 | $(buttons[3]).tooltip(); 124 | $(buttons[4]).tooltip(); 125 | $(buttons[5]).tooltip(); 126 | $(buttons[6]).tooltip(); 127 | $(buttons[7]).tooltip(); 128 | $(buttons[8]).tooltip(); 129 | $(buttons[9]).tooltip(); 130 | $(buttons[10]).tooltip(); 131 | $(buttons[11]).tooltip(); 132 | $(buttons[12]).tooltip(); 133 | 134 | }; -------------------------------------------------------------------------------- /js/setupViewport.js: -------------------------------------------------------------------------------- 1 | function setupViewport(element, stack, image) { 2 | // Display the image on the viewer element 3 | cornerstone.displayImage(element, image); 4 | 5 | // If it's a movie (has frames), then play the clip 6 | if (stack.frameRate !== undefined) { 7 | cornerstone.playClip(element, stack.frameRate); 8 | } 9 | 10 | // Activate mouse clicks, mouse wheel and touch 11 | cornerstoneTools.mouseInput.enable(element); 12 | cornerstoneTools.mouseWheelInput.enable(element); 13 | cornerstoneTools.touchInput.enable(element); 14 | 15 | // Enable all tools we want to use with this element 16 | cornerstoneTools.wwwc.activate(element, 1); // ww/wc is the default tool for left mouse button 17 | cornerstoneTools.pan.activate(element, 2); // pan is the default tool for middle mouse button 18 | cornerstoneTools.zoom.activate(element, 4); // zoom is the default tool for right mouse button 19 | cornerstoneTools.probe.enable(element); 20 | cornerstoneTools.length.enable(element); 21 | cornerstoneTools.ellipticalRoi.enable(element); 22 | cornerstoneTools.rectangleRoi.enable(element); 23 | cornerstoneTools.wwwcTouchDrag.activate(element); 24 | cornerstoneTools.zoomTouchPinch.activate(element); 25 | 26 | // Stack tools 27 | cornerstoneTools.addStackStateManager(element, ['playClip']); 28 | cornerstoneTools.addToolState(element, 'stack', stack); 29 | cornerstoneTools.stackScrollWheel.activate(element); 30 | cornerstoneTools.stackPrefetch.enable(element); 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /js/setupViewportOverlays.js: -------------------------------------------------------------------------------- 1 | function setupViewportOverlays(element, data) { 2 | var parent = $(element).parent(); 3 | 4 | // Get the overlays 5 | var childDivs = $(parent).find('.overlay'); 6 | var topLeft = $(childDivs[0]).find('div'); 7 | var topRight = $(childDivs[1]).find('div'); 8 | var bottomLeft = $(childDivs[2]).find('div'); 9 | var bottomRight = $(childDivs[3]).find('div'); 10 | 11 | // Set the overlay text 12 | $(topLeft[0]).text(data.patientName); 13 | $(topLeft[1]).text(data.patientId); 14 | $(topRight[0]).text(data.studyDescription); 15 | $(topRight[1]).text(data.studyDate); 16 | 17 | 18 | // On new image (displayed?) 19 | function onNewImage(e, eventData) { 20 | // If we are currently playing a clip then update the FPS 21 | // Get the state of the 'playClip tool' 22 | var playClipToolData = cornerstoneTools.getToolState(element, 'playClip'); 23 | 24 | // If playing a clip ... 25 | if (playClipToolData !== undefined && playClipToolData.data.length > 0 && playClipToolData.data[0].intervalId !== undefined && eventData.frameRate !== undefined) { 26 | 27 | // Update FPS 28 | $(bottomLeft[0]).text("FPS: " + Math.round(eventData.frameRate)); 29 | console.log('frameRate: ' + e.frameRate); 30 | 31 | } else { 32 | // Set FPS empty if not playing a clip 33 | if ($(bottomLeft[0]).text().length > 0) { 34 | $(bottomLeft[0]).text(""); 35 | } 36 | } 37 | 38 | var toolData = cornerstoneTools.getToolState(element, 'stack'); 39 | if(toolData === undefined || toolData.data === undefined || toolData.data.length === 0) { 40 | return; 41 | } 42 | var stack = toolData.data[0]; 43 | 44 | // Update Image number overlay 45 | $(bottomLeft[2]).text("Image # " + (stack.currentImageIdIndex + 1) + "/" + stack.imageIds.length); 46 | } 47 | // Add a CornerstoneNewImage event listener on the 'element' (viewer) (?) 48 | $(element).on("CornerstoneNewImage", onNewImage); 49 | 50 | 51 | // On image rendered 52 | function onImageRendered(e, eventData) { 53 | // Set zoom overlay text 54 | $(bottomRight[0]).text("Zoom:" + eventData.viewport.scale.toFixed(2)); 55 | // Set WW/WL overlay text 56 | $(bottomRight[1]).text("WW/WL:" + Math.round(eventData.viewport.voi.windowWidth) + "/" + Math.round(eventData.viewport.voi.windowCenter)); 57 | // Set render time overlay text 58 | $(bottomLeft[1]).text("Render Time:" + eventData.renderTimeInMs + " ms"); 59 | } 60 | // Add a CornerstoneImageRendered event listener on the 'element' (viewer) (?) 61 | $(element).on("CornerstoneImageRendered", onImageRendered); 62 | 63 | 64 | } -------------------------------------------------------------------------------- /lib/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /lib/cornerstone.css: -------------------------------------------------------------------------------- 1 | /*! cornerstone - v0.7.3 - 2015-04-04 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */ 2 | .cornerstone-enabled-image { 3 | 4 | /* prevent text selection from occurring when dragging the mouse on the div */ 5 | /* http://stackoverflow.com/questions/826782/css-rule-to-disable-text-selection-highlighting */ 6 | -webkit-touch-callout: none; 7 | -webkit-user-select: none; 8 | -khtml-user-select: none; 9 | -moz-user-select: none; 10 | -ms-user-select: none; 11 | user-select: none; 12 | 13 | /* force the cursor to always be the default arrow. This prevents it from changing to an ibar when it is 14 | over HTML based text overlays (four corners */ 15 | cursor:default; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /lib/cornerstone.min.css: -------------------------------------------------------------------------------- 1 | /*! cornerstone - v0.8.1 - 2015-06-16 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */.cornerstone-enabled-image{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default} -------------------------------------------------------------------------------- /lib/cornerstoneFileImageLoader.js: -------------------------------------------------------------------------------- 1 | /*! cornerstone-file-image-loader | https://github.com/chafey/cornerstoneFileImageLoader */ 2 | //这是 DICOM P10文件的基础映像加载程序。它目前不支持压缩传输语法或大的 endian 传输语法。 3 | //它将支持隐式小端转换语法,但显式小端更倾向于避免与SQ元素相关的任何解析问题。 4 | 5 | var cornerstoneFileImageLoader = (function ($, cornerstone, cornerstoneFileImageLoader) { 6 | 7 | "use strict"; 8 | 9 | if(cornerstoneFileImageLoader === undefined) { 10 | cornerstoneFileImageLoader = {}; 11 | } 12 | 13 | 14 | 15 | function isColorImage(photoMetricInterpretation) 16 | { 17 | if(photoMetricInterpretation === "RGB" || 18 | photoMetricInterpretation === "PALETTE COLOR" || 19 | photoMetricInterpretation === "YBR_FULL" || 20 | photoMetricInterpretation === "YBR_FULL_422" || 21 | photoMetricInterpretation === "YBR_PARTIAL_422" || 22 | photoMetricInterpretation === "YBR_PARTIAL_420" || 23 | photoMetricInterpretation === "YBR_RCT") 24 | { 25 | return true; 26 | } 27 | else 28 | { 29 | return false; 30 | } 31 | } 32 | 33 | function createImageObject(dataSet, imageId, frame) 34 | { 35 | if(frame === undefined) { 36 | frame = 0; 37 | } 38 | 39 | // make the image based on whether it is color or not 40 | var photometricInterpretation = dataSet.string('x00280004'); 41 | var isColor = isColorImage(photometricInterpretation); 42 | if(isColor === false) { 43 | return cornerstoneWADOImageLoader.makeGrayscaleImage(imageId, dataSet, dataSet.byteArray, photometricInterpretation, frame); 44 | } else { 45 | return cornerstoneWADOImageLoader.makeColorImage(imageId, dataSet, dataSet.byteArray, photometricInterpretation, frame); 46 | } 47 | } 48 | 49 | var multiFrameCacheHack = {}; 50 | 51 | // 加载给定imageId的图像 52 | // wado url示例: 53 | // http://localhost:3333/wado?requestType=WADO&studyUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.1&seriesUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.2&objectUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075557.1&contentType=application%2Fdicom&transferSyntax=1.2.840.10008.1.2.1 54 | // NOTE: 如果不指定transferSyntax,那么这个实例将会以显式的小Endian传输语法返回,但是Osirix没有这样做,而且似乎会返回它所存储的传输语法 55 | function loadImage(imageId) { 56 | // 创建一个延迟对象 57 | // TODO:考虑不使用jquery进行延迟——可能是cujo的when库 58 | var deferred = $.Deferred(); 59 | 60 | //通过解析imageId的url模式和框架索引来构建url 61 | var url = imageId; 62 | url = url.substring(12); 63 | var frameIndex = url.indexOf('frame='); 64 | var frame; 65 | if(frameIndex !== -1) { 66 | var frameStr = url.substr(frameIndex + 6); 67 | frame = parseInt(frameStr); 68 | url = url.substr(0, frameIndex-1); 69 | } 70 | 71 | // if multiframe and cached, use the cached data set to extract the frame 72 | if(frame !== undefined && 73 | multiFrameCacheHack.hasOwnProperty(url)) 74 | { 75 | var dataSet = multiFrameCacheHack[url]; 76 | var imagePromise = createImageObject(dataSet, imageId, frame); 77 | imagePromise.then(function(image) { 78 | deferred.resolve(image); 79 | }, function() { 80 | deferred.reject(); 81 | }); 82 | return deferred; 83 | } 84 | 85 | var fileIndex = parseInt(url); 86 | var file = cornerstoneFileImageLoader.getFile(fileIndex); 87 | if(file === undefined) { 88 | deferred.reject('unknown file index ' + url); 89 | return deferred; 90 | } 91 | 92 | // Read the DICOM Data 93 | var fileReader = new FileReader(); 94 | fileReader.onload = function(e) { 95 | // Parse the DICOM File 96 | var dicomPart10AsArrayBuffer = e.target.result; 97 | var byteArray = new Uint8Array(dicomPart10AsArrayBuffer); 98 | var dataSet = dicomParser.parseDicom(byteArray); 99 | 100 | // if multiframe, cache the parsed data set to speed up subsequent 101 | // requests for the other frames 102 | if(frame !== undefined) { 103 | multiFrameCacheHack[url] = dataSet; 104 | } 105 | 106 | var imagePromise = createImageObject(dataSet, imageId, frame); 107 | imagePromise.then(function(image) { 108 | deferred.resolve(image); 109 | }, function() { 110 | deferred.reject(); 111 | }); 112 | }; 113 | fileReader.readAsArrayBuffer(file); 114 | 115 | return deferred; 116 | } 117 | 118 | // 使用http和https前缀,这样我们就可以直接使用wado URL 119 | cornerstone.registerImageLoader('dicomfile', loadImage); 120 | 121 | return cornerstoneFileImageLoader; 122 | }($, cornerstone, cornerstoneFileImageLoader)); 123 | /** 124 | */ 125 | var cornerstoneFileImageLoader = (function (cornerstoneFileImageLoader) { 126 | 127 | "use strict"; 128 | 129 | if(cornerstoneFileImageLoader === undefined) { 130 | cornerstoneFileImageLoader = {}; 131 | } 132 | 133 | var files = []; 134 | 135 | function addFile(file) { 136 | var fileIndex = files.push(file); 137 | return fileIndex - 1; 138 | } 139 | 140 | function getFile(index) { 141 | return files[index]; 142 | } 143 | 144 | function purge() { 145 | files = []; 146 | } 147 | 148 | // module exports 149 | cornerstoneFileImageLoader.addFile = addFile; 150 | cornerstoneFileImageLoader.getFile = getFile; 151 | cornerstoneFileImageLoader.purge = purge; 152 | 153 | return cornerstoneFileImageLoader; 154 | }(cornerstoneFileImageLoader)); -------------------------------------------------------------------------------- /lib/cornerstoneWebImageLoader.js: -------------------------------------------------------------------------------- 1 | /*! cornerstoneWebImageLoader - v0.5.2 - 2015-09-06 | (c) 2015 Chris Hafey | https://github.com/chafey/cornerstoneWebImageLoader */ 2 | cornerstoneWebImageLoader = {}; 3 | // 4 | // This is a cornerstone image loader for web images such as PNG and JPEG 5 | // 6 | 7 | (function ($, cornerstone, cornerstoneWebImageLoader) { 8 | 9 | "use strict"; 10 | 11 | var canvas = document.createElement('canvas'); 12 | var lastImageIdDrawn = ""; 13 | 14 | 15 | var options = { 16 | // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) 17 | beforeSend : function(xhr) {} 18 | }; 19 | 20 | function createImageObject(image, imageId) 21 | { 22 | // extract the attributes we need 23 | var rows = image.naturalHeight; 24 | var columns = image.naturalWidth; 25 | 26 | function getPixelData() 27 | { 28 | var imageData = getImageData(); 29 | var imageDataData = imageData.data; 30 | var numPixels = image.naturalHeight * image.naturalWidth; 31 | var storedPixelData = new Uint8Array(numPixels * 4); 32 | var imageDataIndex = 0; 33 | var storedPixelDataIndex = 0; 34 | for(var i=0; i < numPixels; i++) { 35 | storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; 36 | storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; 37 | storedPixelData[storedPixelDataIndex++] = imageDataData[imageDataIndex++]; 38 | storedPixelData[storedPixelDataIndex++] = 255; // alpha 39 | imageDataIndex++; 40 | } 41 | return storedPixelData; 42 | } 43 | 44 | function getImageData() 45 | { 46 | var context; 47 | if(lastImageIdDrawn !== imageId) { 48 | canvas.height = image.naturalHeight; 49 | canvas.width = image.naturalWidth; 50 | context = canvas.getContext('2d'); 51 | context.drawImage(image, 0, 0); 52 | lastImageIdDrawn = imageId; 53 | } 54 | else { 55 | context = canvas.getContext('2d'); 56 | } 57 | var imageData = context.getImageData(0, 0, image.naturalWidth, image.naturalHeight); 58 | return imageData; 59 | } 60 | 61 | function getCanvas() 62 | { 63 | if(lastImageIdDrawn === imageId) { 64 | return canvas; 65 | } 66 | 67 | canvas.height = image.naturalHeight; 68 | canvas.width = image.naturalWidth; 69 | var context = canvas.getContext('2d'); 70 | context.drawImage(image, 0, 0); 71 | lastImageIdDrawn = imageId; 72 | return canvas; 73 | } 74 | 75 | function getImage() 76 | { 77 | return image; 78 | } 79 | 80 | // Extract the various attributes we need 81 | var imageObject = { 82 | imageId : imageId, 83 | minPixelValue : 0, // calculated below 84 | maxPixelValue : 255, // calculated below 85 | slope: 1.0, 86 | intercept: 0, 87 | windowCenter : 127, 88 | windowWidth : 256, 89 | render: cornerstone.renderColorImage, 90 | getPixelData: getPixelData, 91 | getImageData: getImageData, 92 | getCanvas: getCanvas, 93 | getImage: getImage, 94 | //storedPixelData: extractStoredPixels(image), 95 | rows: rows, 96 | columns: columns, 97 | height: rows, 98 | width: columns, 99 | color: true, 100 | columnPixelSpacing: 1.0, 101 | rowPixelSpacing: 1.0, 102 | invert: false, 103 | sizeInBytes : rows * columns * 4 // we don't know for sure so we over estimate to be safe 104 | }; 105 | 106 | return imageObject; 107 | } 108 | 109 | // Loads an image given a url to an image 110 | function loadImage(imageId) { 111 | 112 | // create a deferred object 113 | var deferred = $.Deferred(); 114 | 115 | var image = new Image(); 116 | 117 | var xhr = new XMLHttpRequest(); 118 | xhr.responseType = "arraybuffer"; 119 | xhr.open("GET", imageId, true); 120 | options.beforeSend(xhr); 121 | xhr.onload = function(e) { 122 | var arrayBufferView = new Uint8Array(this.response); 123 | var blob = new Blob([arrayBufferView], {type: "image/jpeg"}); 124 | var urlCreator = window.URL || window.webkitURL; 125 | var imageUrl = urlCreator.createObjectURL(blob); 126 | image.src = imageUrl; 127 | image.onload = function() { 128 | var imageObject = createImageObject(image, imageId); 129 | deferred.resolve(imageObject); 130 | urlCreator.revokeObjectURL(imageUrl); 131 | }; 132 | image.onerror = function() { 133 | urlCreator.revokeObjectURL(imageUrl); 134 | deferred.reject(); 135 | }; 136 | } 137 | xhr.onprogress = function(oProgress) { 138 | 139 | if (oProgress.lengthComputable) { //evt.loaded the bytes browser receive 140 | //evt.total the total bytes seted by the header 141 | // 142 | var loaded = oProgress.loaded; 143 | var total = oProgress.total; 144 | var percentComplete = Math.round((loaded / total)*100); 145 | 146 | $(cornerstone).trigger('CornerstoneImageLoadProgress', { 147 | imageId: imageId, 148 | loaded: loaded, 149 | total: total, 150 | percentComplete: percentComplete 151 | }); 152 | } 153 | }; 154 | xhr.send(); 155 | return deferred; 156 | } 157 | 158 | function configure(opts) { 159 | options = opts; 160 | } 161 | 162 | // steam the http and https prefixes so we can use standard web urls directly 163 | cornerstone.registerImageLoader('http', loadImage); 164 | cornerstone.registerImageLoader('https', loadImage); 165 | cornerstoneWebImageLoader.configure = configure; 166 | return cornerstone; 167 | }($, cornerstone, cornerstoneWebImageLoader)); 168 | -------------------------------------------------------------------------------- /lib/hammer.min.js: -------------------------------------------------------------------------------- 1 | /*! Hammer.JS - v2.0.4 - 2014-09-28 2 | * http://hammerjs.github.io/ 3 | * 4 | * Copyright (c) 2014 Jorik Tangelder; 5 | * Licensed under the MIT license */ 6 | !function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(k(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e-1}function r(a){return a.trim().split(/\s+/g)}function s(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function v(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=E(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=F(d);b.timeStamp=nb(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=J(h,i),b.distance=I(h,i),C(c,b),b.offsetDirection=H(b.deltaX,b.deltaY),b.scale=g?L(g.pointers,d):1,b.rotation=g?K(g.pointers,d):0,D(c,b);var j=a.element;p(b.srcEvent.target,j)&&(j=b.srcEvent.target),b.target=j}function C(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===yb||f.eventType===Ab)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function D(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Bb&&(i>xb||h.velocity===d)){var j=h.deltaX-b.deltaX,k=h.deltaY-b.deltaY,l=G(i,j,k);e=l.x,f=l.y,c=mb(l.x)>mb(l.y)?l.x:l.y,g=H(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function E(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:lb(c/b),y:lb(d/b)}}function G(a,b,c){return{x:b/a||0,y:c/a||0}}function H(a,b){return a===b?Cb:mb(a)>=mb(b)?a>0?Db:Eb:b>0?Fb:Gb}function I(a,b,c){c||(c=Kb);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function J(a,b,c){c||(c=Kb);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function K(a,b){return J(b[1],b[0],Lb)-J(a[1],a[0],Lb)}function L(a,b){return I(b[0],b[1],Lb)/I(a[0],a[1],Lb)}function M(){this.evEl=Nb,this.evWin=Ob,this.allow=!0,this.pressed=!1,y.apply(this,arguments)}function N(){this.evEl=Rb,this.evWin=Sb,y.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function O(){this.evTarget=Ub,this.evWin=Vb,this.started=!1,y.apply(this,arguments)}function P(a,b){var c=t(a.touches),d=t(a.changedTouches);return b&(Ab|Bb)&&(c=u(c.concat(d),"identifier",!0)),[c,d]}function Q(){this.evTarget=Xb,this.targetIds={},y.apply(this,arguments)}function R(a,b){var c=t(a.touches),d=this.targetIds;if(b&(yb|zb)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=t(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return p(a.target,i)}),b===yb)for(e=0;eh&&(b.push(a),h=b.length-1):e&(Ab|Bb)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Tb={touchstart:yb,touchmove:zb,touchend:Ab,touchcancel:Bb},Ub="touchstart",Vb="touchstart touchmove touchend touchcancel";j(O,y,{handler:function(a){var b=Tb[a.type];if(b===yb&&(this.started=!0),this.started){var c=P.call(this,a,b);b&(Ab|Bb)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:tb,srcEvent:a})}}});var Wb={touchstart:yb,touchmove:zb,touchend:Ab,touchcancel:Bb},Xb="touchstart touchmove touchend touchcancel";j(Q,y,{handler:function(a){var b=Wb[a.type],c=R.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:tb,srcEvent:a})}}),j(S,y,{handler:function(a,b,c){var d=c.pointerType==tb,e=c.pointerType==vb;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Ab|Bb)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var Yb=v(jb.style,"touchAction"),Zb=Yb!==d,$b="compute",_b="auto",ac="manipulation",bc="none",cc="pan-x",dc="pan-y";T.prototype={set:function(a){a==$b&&(a=this.compute()),Zb&&(this.manager.element.style[Yb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){l(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),U(a.join(" "))},preventDefaults:function(a){if(!Zb){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=q(d,bc),f=q(d,dc),g=q(d,cc);return e||f&&c&Hb||g&&c&Ib?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var ec=1,fc=2,gc=4,hc=8,ic=hc,jc=16,kc=32;V.prototype={defaults:{},set:function(a){return h(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=Y(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=Y(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=Y(a,this),-1===s(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=Y(a,this);var b=s(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(c.options.event+(b?W(d):""),a)}var c=this,d=this.state;hc>d&&b(!0),b(),d>=hc&&b(!0)},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=kc)},canEmit:function(){for(var a=0;af?Db:Eb,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Cb:0>g?Fb:Gb,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return Z.prototype.attrTest.call(this,a)&&(this.state&fc||!(this.state&fc)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=X(a.direction);b&&this.manager.emit(this.options.event+b,a),this._super.emit.call(this,a)}}),j(_,Z,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[bc]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&fc)},emit:function(a){if(this._super.emit.call(this,a),1!==a.scale){var b=a.scale<1?"in":"out";this.manager.emit(this.options.event+b,a)}}}),j(ab,V,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return[_b]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Ab|Bb)&&!f)this.reset();else if(a.eventType&yb)this.reset(),this._timer=e(function(){this.state=ic,this.tryEmit()},b.time,this);else if(a.eventType&Ab)return ic;return kc},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===ic&&(a&&a.eventType&Ab?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=nb(),this.manager.emit(this.options.event,this._input)))}}),j(bb,Z,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[bc]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&fc)}}),j(cb,Z,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:Hb|Ib,pointers:1},getTouchAction:function(){return $.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Hb|Ib)?b=a.velocity:c&Hb?b=a.velocityX:c&Ib&&(b=a.velocityY),this._super.attrTest.call(this,a)&&c&a.direction&&a.distance>this.options.threshold&&mb(b)>this.options.velocity&&a.eventType&Ab},emit:function(a){var b=X(a.direction);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),j(db,V,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[ac]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance=0)&&t(i,!n)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var s=0,n=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,a=n.call(arguments,1),o=0,r=a.length;r>o;o++)for(i in a[o])s=a[o][i],a[o].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(a){var o="string"==typeof a,r=n.call(arguments,1),h=this;return o?this.each(function(){var i,n=e.data(this,s);return"instance"===a?(h=n,!1):n?e.isFunction(n[a])&&"_"!==a.charAt(0)?(i=n[a].apply(n,r),i!==n&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+a+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+a+"'")}):(r.length&&(a=e.widget.extend.apply(null,[a].concat(r))),this.each(function(){var t=e.data(this,s);t?(t.option(a||{}),t._init&&t._init()):e.data(this,s,new i(a,this))})),h}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var a=!1;e(document).mouseup(function(){a=!1}),e.widget("ui.mouse",{version:"1.11.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!a){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),a=!0,!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button)return this._mouseUp(t);if(!t.which)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),a=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),e.widget("ui.draggable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var i=this.options;return this._blurActiveElement(t),this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(t){this.iframeBlocks=this.document.find(t).map(function(){var t=e(this);return e("
    ").css("position","absolute").appendTo(t.parent()).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(t){var i=this.document[0];if(this.handleElement.is(t.target))try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&e(i.activeElement).blur()}catch(s){}},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===e(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(t),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._normalizeRightBottom(),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_refreshOffsets:function(e){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top}},_mouseDrag:function(t,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return this._unblockFrames(),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.handleElement.is(t.target)&&this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.handleElement.removeClass("ui-draggable-handle")},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper),n=s?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options,a=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,e(a).width()-this.helperProportions.width-this.margins.left,(e(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(e,t){t||(t=this.position);var i="absolute"===e?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(e,t){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=e.pageX,l=e.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.lefti[2]&&(h=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){"y"!==this.options.axis&&"auto"!==this.helper.css("right")&&(this.helper.width(this.helper.width()),this.helper.css("right","auto")),"x"!==this.options.axis&&"auto"!==this.helper.css("bottom")&&(this.helper.height(this.helper.height()),this.helper.css("bottom","auto"))},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s,this],!0),/^(drag|start|stop)/.test(t)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i,s){var n=e.extend({},i,{item:s.element});s.sortables=[],e(s.options.connectToSortable).each(function(){var i=e(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",t,n))})},stop:function(t,i,s){var n=e.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,e.each(s.sortables,function(){var e=this;e.isOver?(e.isOver=0,s.cancelHelperRemoval=!0,e.cancelHelperRemoval=!1,e._storedCSS={position:e.placeholder.css("position"),top:e.placeholder.css("top"),left:e.placeholder.css("left")},e._mouseStop(t),e.options.helper=e.options._helper):(e.cancelHelperRemoval=!0,e._trigger("deactivate",t,n))})},drag:function(t,i,s){e.each(s.sortables,function(){var n=!1,a=this;a.positionAbs=s.positionAbs,a.helperProportions=s.helperProportions,a.offset.click=s.offset.click,a._intersectsWith(a.containerCache)&&(n=!0,e.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==a&&this._intersectsWith(this.containerCache)&&e.contains(a.element[0],this.element[0])&&(n=!1),n})),n?(a.isOver||(a.isOver=1,s._parent=i.helper.parent(),a.currentItem=i.helper.appendTo(a.element).data("ui-sortable-item",!0),a.options._helper=a.options.helper,a.options.helper=function(){return i.helper[0]},t.target=a.currentItem[0],a._mouseCapture(t,!0),a._mouseStart(t,!0,!0),a.offset.click.top=s.offset.click.top,a.offset.click.left=s.offset.click.left,a.offset.parent.left-=s.offset.parent.left-a.offset.parent.left,a.offset.parent.top-=s.offset.parent.top-a.offset.parent.top,s._trigger("toSortable",t),s.dropped=a.element,e.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,a.fromOutside=s),a.currentItem&&(a._mouseDrag(t),i.position=a.position)):a.isOver&&(a.isOver=0,a.cancelHelperRemoval=!0,a.options._revert=a.options.revert,a.options.revert=!1,a._trigger("out",t,a._uiHash(a)),a._mouseStop(t,!0),a.options.revert=a.options._revert,a.options.helper=a.options._helper,a.placeholder&&a.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(t),i.position=s._generatePosition(t,!0),s._trigger("fromSortable",t),s.dropped=!1,e.each(s.sortables,function(){this.refreshPositions()}))})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,i,s){var n=e("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(t,i,s){var n=s.options;n._cursor&&e("body").css("cursor",n._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(t,i,s){var n=s.options;n._opacity&&e(i.helper).css("opacity",n._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(t,i,s){var n=s.options,a=!1,o=s.scrollParentNotHidden[0],r=s.document[0];o!==r&&"HTML"!==o.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+o.offsetHeight-t.pageY=0;c--)h=s.snapElements[c].left-s.margins.left,l=h+s.snapElements[c].width,u=s.snapElements[c].top-s.margins.top,d=u+s.snapElements[c].height,h-m>v||g>l+m||u-m>b||y>d+m||!e.contains(s.snapElements[c].item.ownerDocument,s.snapElements[c].item)?(s.snapElements[c].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(d-y),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-y),a=m>=Math.abs(d-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d-s.helperProportions.height,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[c].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=n||a||o||r||p)}}),e.ui.plugin.add("draggable","stack",{start:function(t,i,s){var n,a=s.options,o=e.makeArray(e(a.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(e(o[0]).css("zIndex"),10)||0,e(o).each(function(t){e(this).css("zIndex",n+t)}),this.css("zIndex",n+o.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(t,i,s){var n=s.options;n._zIndex&&e(i.helper).css("zIndex",n._zIndex)}}),e.ui.draggable,e.widget("ui.droppable",{version:"1.11.4",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,i=this.options,s=i.accept; 7 | this.isover=!1,this.isout=!0,this.accept=e.isFunction(s)?s:function(e){return e.is(s)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){if("accept"===t)this.accept=e.isFunction(i)?i:function(e){return e.is(i)};else if("scope"===t){var s=e.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(t,i)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=e(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(i,{offset:i.element.offset()}),i.options.tolerance,t)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,i){return e>=t&&t+i>e}return function(t,i,s,n){if(!i.offset)return!1;var a=(t.positionAbs||t.position.absolute).left+t.margins.left,o=(t.positionAbs||t.position.absolute).top+t.margins.top,r=a+t.helperProportions.width,h=o+t.helperProportions.height,l=i.offset.left,u=i.offset.top,d=l+i.proportions().width,c=u+i.proportions().height;switch(s){case"fit":return a>=l&&d>=r&&o>=u&&c>=h;case"intersect":return a+t.helperProportions.width/2>l&&d>r-t.helperProportions.width/2&&o+t.helperProportions.height/2>u&&c>h-t.helperProportions.height/2;case"pointer":return e(n.pageY,u,i.proportions().height)&&e(n.pageX,l,i.proportions().width);case"touch":return(o>=u&&c>=o||h>=u&&c>=h||u>o&&h>c)&&(a>=l&&d>=a||r>=l&&d>=r||l>a&&r>d);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance,i),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===n}),a.length&&(s=e(a[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}},e.ui.droppable}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cornerstoneDemo", 3 | "version": "0.1.0", 4 | "description": "Cornerstone Demo", 5 | "keywords": [ 6 | "DICOM", 7 | "WADO", 8 | "cornerstone", 9 | "medical", 10 | "imaging" 11 | ], 12 | "author": "Chris Hafey", 13 | "homepage": "https://github.com/chafey/cornerstoneDemo", 14 | "licnense": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/chafey/cornerstoneDemo.git" 18 | }, 19 | "scripts": { 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "devDependencies": { 23 | "grunt-contrib-copy": "0.4.x", 24 | "grunt-contrib-qunit": "^0.4.0", 25 | "grunt-contrib-concat": "^0.3.0", 26 | "grunt-contrib-watch": "^0.6.1", 27 | "grunt-contrib-clean": "^0.5.0", 28 | "grunt-contrib-jshint": "^0.8.0", 29 | "grunt-contrib-uglify": "^0.4.0", 30 | "load-grunt-tasks": "^0.2.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /studies/crstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "MISTER^CR", 3 | "patientId": "9227465", 4 | "studyDate": "20010109", 5 | "modality": "CR", 6 | "studyDescription": "pelvis", 7 | "numImages": 2, 8 | "studyId": "crstudy", 9 | "seriesList": [{ 10 | "seriesDescription": "Pelvis PA", 11 | "seriesNumber": "1", 12 | "instanceList": [{ 13 | "imageId": "1.5191KB.DCM" 14 | }] 15 | }, 16 | { 17 | "seriesDescription": "PELVIS LAT", 18 | "seriesNumber": "1", 19 | "instanceList": [{ 20 | "imageId": "2.516KB.DCM" 21 | }] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /studies/ctstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "MISTER^CT", 3 | "patientId": "2178309", 4 | "studyDate": "20010105", 5 | "modality": "CT", 6 | "studyDescription": "CHEST", 7 | "numImages": 111, 8 | "studyId": "ctstudy", 9 | "seriesList": [{ 10 | "seriesUid": "1.5191", 11 | "seriesDescription": "HELICAL CHEST", 12 | "seriesNumber": "2", 13 | "instanceList": [{ 14 | "imageId": "CTStudy/1.5191KB.DCM" 15 | }, 16 | { 17 | "imageId": "CTStudy/2.516KB.DCM" 18 | }, 19 | { 20 | "imageId": "CTStudy/3.79KB.DCM" 21 | }, 22 | { 23 | "imageId": "CTStudy/test.dcm" 24 | } 25 | ] 26 | }, 27 | { 28 | "seriesUid": "2.516", 29 | "seriesDescription": "SCOUT PA", 30 | "seriesNumber": "1", 31 | "instanceList": [{ 32 | "imageId": "CTStudy/2.516KB.DCM" 33 | }] 34 | }, 35 | { 36 | "seriesUid": "3.79", 37 | "seriesDescription": "SCOUT LAT", 38 | "seriesNumber": "1.1", 39 | "instanceList": [{ 40 | "imageId": "CTStudy/3.79KB.DCM" 41 | }] 42 | }, 43 | { 44 | "seriesUid": "5", 45 | "seriesDescription": "PATHOLOGY", 46 | "seriesNumber": "5", 47 | "instanceList": [{ 48 | "imageId": "http://127.0.0.1/testDICOM/JPG/1.jpg" 49 | }] 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /studies/dermatology.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "Dermatology", 3 | "patientId": "922746522", 4 | "studyDate": "20010109", 5 | "modality": "DERM", 6 | "studyDescription": "Skin Rash", 7 | "numImages": 2, 8 | "studyId": "dermatology", 9 | "seriesList": [{ 10 | "seriesDescription": "Back", 11 | "seriesNumber": "28858", 12 | "instanceList": [{ 13 | "imageId": "http://127.0.0.1/testDICOM/JPG/1.jpg" 14 | }] 15 | }] 16 | } -------------------------------------------------------------------------------- /studies/dxstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "MISTER^CR", 3 | "patientId": "9227465", 4 | "studyDate": "20010109", 5 | "modality": "DX", 6 | "studyDescription": "CHEST", 7 | "numImages": 2, 8 | "studyId": "dxstudy", 9 | "seriesList": [{ 10 | "seriesDescription": "PA", 11 | "seriesNumber": "28858", 12 | "instanceList": [{ 13 | "imageId": "DXStudy/3.79KB.DCM" 14 | }] 15 | }, 16 | { 17 | "seriesDescription": "LAT", 18 | "seriesNumber": "28860", 19 | "instanceList": [{ 20 | "imageId": "DXStudy/2.516KB.DCM" 21 | }] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /studies/mgstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "MS^MG", 3 | "patientId": "1346269", 4 | "studyDate": "20010109", 5 | "modality": "MG", 6 | "studyDescription": "MAMMOGRAM", 7 | "numImages": 8, 8 | "studyId": "mgstudy", 9 | "seriesList": [{ 10 | "seriesDescription": "MAMMOGRAM", 11 | "seriesNumber": "700", 12 | "instanceList": [{ 13 | "imageId": "MGStudy/1.5191KB.DCM" 14 | }] 15 | }, 16 | { 17 | "seriesDescription": "MAMMOGRAM", 18 | "seriesNumber": "700", 19 | "instanceList": [{ 20 | "imageId": "MGStudy/2.516KB.DCM" 21 | }] 22 | }, 23 | { 24 | "seriesDescription": "MAMMOGRAM", 25 | "seriesNumber": "700", 26 | "instanceList": [{ 27 | "imageId": "MGStudy/3.79KB.DCM" 28 | }] 29 | }, 30 | { 31 | "seriesDescription": "MAMMOGRAM", 32 | "seriesNumber": "700", 33 | "instanceList": [{ 34 | "imageId": "MGStudy/3.79KB.DCM" 35 | }] 36 | }, 37 | { 38 | "seriesDescription": "BILATERAL", 39 | "seriesNumber": "699", 40 | "instanceList": [{ 41 | "imageId": "MGStudy/3.79KB.DCM" 42 | }] 43 | }, 44 | { 45 | "seriesDescription": "BILATERAL", 46 | "seriesNumber": "699", 47 | "instanceList": [{ 48 | "imageId": "MGStudy/1.5191KB.DCM" 49 | }] 50 | }, 51 | { 52 | "seriesDescription": "BILATERAL", 53 | "seriesNumber": "699", 54 | "instanceList": [{ 55 | "imageId": "MGStudy/1.5191KB.DCM" 56 | }] 57 | }, 58 | { 59 | "seriesDescription": "BILATERAL", 60 | "seriesNumber": "699", 61 | "instanceList": [{ 62 | "imageId": "MGStudy/1.5191KB.DCM" 63 | }] 64 | }, 65 | { 66 | "seriesDescription": "BILATERAL", 67 | "seriesNumber": "699", 68 | "instanceList": [{ 69 | "imageId": "MGStudy/test.dcm" 70 | }] 71 | } 72 | ] 73 | } -------------------------------------------------------------------------------- /studies/mrstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "MISTER^MR", 3 | "patientId": "832040", 4 | "studyDate": "20010108", 5 | "modality": "MR", 6 | "studyDescription": "BRAIN SELLA", 7 | "numImages": 17, 8 | "studyId": "mrstudy", 9 | "seriesList": [{ 10 | "seriesDescription": "3-PLANE LOC", 11 | "seriesNumber": "1", 12 | "instanceList": [{ 13 | "imageId": "MRStudy/MR000000.dcm" 14 | }, 15 | { 16 | "imageId": "MRStudy/MR000001.dcm" 17 | }, 18 | { 19 | "imageId": "MRStudy/MR000002.dcm" 20 | }, 21 | { 22 | "imageId": "MRStudy/MR000003.dcm" 23 | }, 24 | { 25 | "imageId": "MRStudy/MR000003.dcm" 26 | }, 27 | { 28 | "imageId": "MRStudy/MR000004.dcm" 29 | }, 30 | { 31 | "imageId": "MRStudy/MR000005.dcm" 32 | }, 33 | { 34 | "imageId": "MRStudy/MR000006.dcm" 35 | }, 36 | { 37 | "imageId": "MRStudy/MR000007.dcm" 38 | }, 39 | { 40 | "imageId": "MRStudy/MR000008.dcm" 41 | } 42 | ] 43 | }, 44 | { 45 | "seriesDescription": "SAG T-1", 46 | "seriesNumber": "2", 47 | "instanceList": [{ 48 | "imageId": "MRStudy/MR000009.dcm" 49 | }, 50 | { 51 | "imageId": "MRStudy/MR000010.dcm" 52 | }, 53 | { 54 | "imageId": "MRStudy/MR000011.dcm" 55 | }, 56 | { 57 | "imageId": "MRStudy/MR000012.dcm" 58 | } 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /studies/ptctstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "Patient^Anonymous", 3 | "patientId": "12345678", 4 | "studyDate": "20100510", 5 | "modality": "PTCT", 6 | "studyDescription": "Neck^HeadNeckPETCT", 7 | "numImages": 261, 8 | "studyId": "ptctstudy", 9 | "seriesList": [{ 10 | "seriesUid": "1.3.6.1.4.1.25403.52237031786.3872.20100510032220.2", 11 | "seriesDescription": "CT HeadNeck 5.0 H30s", 12 | "seriesNumber": "2", 13 | "instanceList": [{ 14 | "imageId": "PTCTStudy/1.5191KB.DCM" 15 | }, 16 | { 17 | "imageId": "PTCTStudy/2.516KB.DCM" 18 | }, 19 | { 20 | "imageId": "PTCTStudy/3.79KB.DCM" 21 | }, 22 | { 23 | "imageId": "PTCTStudy/test.dcm" 24 | } 25 | ] 26 | }, 27 | { 28 | "seriesUid": "1.3.6.1.4.1.25403.52237031786.3872.20100510032225.14", 29 | "seriesDescription": "PET WB-uncorrected", 30 | "seriesNumber": "605", 31 | "instanceList": [{ 32 | "imageId": "PTCTStudy/1.5191KB.DCM" 33 | }, 34 | { 35 | "imageId": "PTCTStudy/2.516KB.DCM" 36 | }, 37 | { 38 | "imageId": "PTCTStudy/3.79KB.DCM" 39 | }, 40 | { 41 | "imageId": "PTCTStudy/test.dcm" 42 | } 43 | ] 44 | }, 45 | { 46 | 47 | "seriesUid": "1.3.6.1.4.1.25403.52237031786.3872.20100510032227.20", 48 | "seriesDescription": "PET WB", 49 | "seriesNumber": "606", 50 | "instanceList": [{ 51 | "imageId": "PTCTStudy/1.5191KB.DCM" 52 | }, 53 | { 54 | "imageId": "PTCTStudy/2.516KB.DCM" 55 | }, 56 | { 57 | "imageId": "PTCTStudy/3.79KB.DCM" 58 | }, 59 | { 60 | "imageId": "PTCTStudy/test.dcm" 61 | } 62 | ] 63 | } 64 | ] 65 | } -------------------------------------------------------------------------------- /studies/usdopplerstudy.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "US Echo", 3 | "patientId": "", 4 | "studyDate": "20120418", 5 | "modality": "US", 6 | "studyDescription": "US Dopler", 7 | "numImages": 22, 8 | "studyId": "usdopplerstudy", 9 | "seriesList": [{ 10 | "seriesDescription": "Image #1", 11 | "seriesNumber": "1", 12 | "instanceList": [{ 13 | "imageId": "USDoppler/1.5191KB.DCM" 14 | }] 15 | }, 16 | { 17 | "seriesDescription": "Image #2", 18 | "seriesNumber": "2", 19 | "instanceList": [{ 20 | "imageId": "USDoppler/2.516KB.DCM" 21 | }] 22 | }, 23 | { 24 | "seriesDescription": "Image #3", 25 | "seriesNumber": "3", 26 | "instanceList": [{ 27 | "imageId": "USDoppler/3.79KB.DCM" 28 | }] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /studies/usjpeg.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientName": "US JPEG", 3 | "patientId": "123454321", 4 | "studyDate": "20120418", 5 | "modality": "US", 6 | "studyDescription": "US FETAL SURVEY", 7 | "numImages": 22, 8 | "studyId": "usjpeg", 9 | "seriesList": [{ 10 | "seriesDescription": "Image #1", 11 | "seriesNumber": "1", 12 | "instanceList": [{ 13 | "imageId": "GEOB/1.5191KB.DCM" 14 | }] 15 | }, 16 | { 17 | "seriesDescription": "Image #2", 18 | "seriesNumber": "2", 19 | "instanceList": [{ 20 | "imageId": "GEOB/2.516KB.DCM" 21 | }] 22 | }, 23 | { 24 | "seriesDescription": "Image #3 (clip)", 25 | "seriesNumber": "3", 26 | "numberOfFrames": 96, 27 | "frameRate": 31, 28 | "instanceList": [{ 29 | "imageId": "GEOB/3.79KB.DCM" 30 | }] 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /studyList.json: -------------------------------------------------------------------------------- 1 | { 2 | "studyList": [{ 3 | "patientName": "患者^MR", 4 | "patientId": "832040", 5 | "studyDate": "20010108", 6 | "modality": "MR", 7 | "studyDescription": "BRAIN SELLA", 8 | "numImages": 17, 9 | "studyId": "mrstudy" 10 | }, 11 | { 12 | "patientName": "患者^CT", 13 | "patientId": "2178309", 14 | "studyDate": "20010105", 15 | "modality": "CT", 16 | "studyDescription": "CHEST", 17 | "numImages": 111, 18 | "studyId": "ctstudy" 19 | }, 20 | { 21 | "patientName": "患者^CR", 22 | "patientId": "9227465", 23 | "studyDate": "20010109", 24 | "modality": "CR", 25 | "studyDescription": "pelvis", 26 | "numImages": 2, 27 | "studyId": "crstudy" 28 | }, 29 | { 30 | "patientName": "患者^DX", 31 | "patientId": "3524578", 32 | "studyDate": "20010109", 33 | "modality": "DX", 34 | "studyDescription": "CHEST", 35 | "numImages": 2, 36 | "studyId": "dxstudy" 37 | }, 38 | { 39 | "patientName": "患者^MG", 40 | "patientId": "1346269", 41 | "studyDate": "20010109", 42 | "modality": "MG", 43 | "studyDescription": "MAMMOGRAM", 44 | "numImages": 8, 45 | "studyId": "mgstudy" 46 | }, 47 | { 48 | "patientName": "患者^Anonymous", 49 | "patientId": "12345678", 50 | "studyDate": "20100510", 51 | "modality": "PTCT", 52 | "studyDescription": "Neck^HeadNeckPETCT", 53 | "numImages": 261, 54 | "studyId": "ptctstudy" 55 | }, 56 | { 57 | "patientName": "US Echo", 58 | "patientId": "", 59 | "studyDate": "20120418", 60 | "modality": "US", 61 | "studyDescription": "US Dopler", 62 | "numImages": 22, 63 | "studyId": "usdopplerstudy" 64 | }, 65 | { 66 | "patientName": "Dermatology", 67 | "patientId": "922746522", 68 | "studyDate": "20010109", 69 | "modality": "DERM", 70 | "studyDescription": "Skin Rash", 71 | "numImages": 2, 72 | "studyId": "dermatology" 73 | }, 74 | { 75 | "patientName": "US JPEG", 76 | "patientId": "123454321", 77 | "studyDate": "20120418", 78 | "modality": "US", 79 | "studyDescription": "US FETAL SURVEY", 80 | "numImages": 22, 81 | "studyId": "usjpeg" 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /studyViewer.html: -------------------------------------------------------------------------------- 1 |