├── _config.yml ├── .gitignore ├── js ├── help.js ├── about.js ├── forEachViewport.js ├── loadTemplate.js ├── disableAllTools.js ├── imageViewer.js ├── setupViewport.js ├── displayThumbnail.js ├── cornerstoneDemo.js ├── setupViewportOverlays.js ├── setupButtons.js └── loadStudy.js ├── lib ├── cornerstone.min.css ├── cornerstone.css ├── cornerstoneFileImageLoader.js ├── cornerstoneWebImageLoader.js ├── jquery-ui.min.css ├── hammer.min.js ├── bootstrap.min.js └── jquery-ui.min.js ├── studies ├── dermatology.json ├── crstudy.json ├── dxstudy.json ├── usdopplerstudy.json ├── usjpeg.json ├── ctstudy.json ├── mrstudy.json ├── ptctstudy.json └── mgstudy.json ├── package.json ├── templates ├── viewport.html ├── help.html ├── about.html └── studyViewer.html ├── bower.json ├── README.md ├── gruntfile.js ├── studyList.json ├── css └── cornerstoneDemo.css ├── architecture.html ├── studyViewer.html └── index.html /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | .idea -------------------------------------------------------------------------------- /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/about.js: -------------------------------------------------------------------------------- 1 | loadTemplate("templates/about.html", function(element) { 2 | $('body').append(element); 3 | $("#about").click(function() { 4 | $("#aboutModal").modal(); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /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} -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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/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/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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /templates/viewport.html: -------------------------------------------------------------------------------- 1 |
7 | 8 |
9 | 10 | 11 |
12 |
Patient Name
13 |
Patient Id
14 |
15 | 16 |
17 |
Study Description
18 |
Study Date
19 |
20 | 21 |
22 |
FPS:
23 |
Render Time:
24 |
Image #:
25 |
26 | 27 |
28 |
Zoom:
29 |
WW/WC:
30 |
31 |
32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /templates/help.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | 29 | 30 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | }; -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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) 的 提醒 邮件地址已更正 -------------------------------------------------------------------------------- /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推源主标签 -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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 | }); -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /templates/studyViewer.html: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /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 | }; -------------------------------------------------------------------------------- /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)); -------------------------------------------------------------------------------- /studyViewer.html: -------------------------------------------------------------------------------- 1 | ',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/jquery-ui.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.4 - 2015-06-26 2 | * http://jqueryui.com 3 | * Includes: core.js, widget.js, mouse.js, draggable.js, droppable.js 4 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=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}); --------------------------------------------------------------------------------