init
Too many changes to show.
To preserve performance only 1000 of 1000+ files are displayed.
84 Bytes
2.74 KB
2.62 KB
1.63 KB
1.58 KB
| 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | ||
| 2 | "http://www.w3.org/TR/html4/loose.dtd"> | ||
| 3 | <html> | ||
| 4 | <head> | ||
| 5 | <title></title> | ||
| 6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | ||
| 7 | <script type="text/javascript" src="../internal.js"></script> | ||
| 8 | <link rel="stylesheet" type="text/css" href="video.css" /> | ||
| 9 | </head> | ||
| 10 | <body> | ||
| 11 | <div class="wrapper"> | ||
| 12 | <div id="videoTab"> | ||
| 13 | <div id="tabHeads" class="tabhead"> | ||
| 14 | <span tabSrc="video" class="focus" data-content-id="video"><var id="lang_tab_insertV"></var></span> | ||
| 15 | <span tabSrc="upload" data-content-id="upload"><var id="lang_tab_uploadV"></var></span> | ||
| 16 | </div> | ||
| 17 | <div id="tabBodys" class="tabbody"> | ||
| 18 | <div id="video" class="panel focus"> | ||
| 19 | <table><tr><td><label for="videoUrl" class="url"><var id="lang_video_url"></var></label></td><td><input id="videoUrl" type="text"></td></tr></table> | ||
| 20 | <div id="preview"></div> | ||
| 21 | <div id="videoInfo"> | ||
| 22 | <fieldset> | ||
| 23 | <legend><var id="lang_video_size"></var></legend> | ||
| 24 | <table> | ||
| 25 | <tr><td><label for="videoWidth"><var id="lang_videoW"></var></label></td><td><input class="txt" id="videoWidth" type="text"/></td></tr> | ||
| 26 | <tr><td><label for="videoHeight"><var id="lang_videoH"></var></label></td><td><input class="txt" id="videoHeight" type="text"/></td></tr> | ||
| 27 | </table> | ||
| 28 | </fieldset> | ||
| 29 | <fieldset> | ||
| 30 | <legend><var id="lang_alignment"></var></legend> | ||
| 31 | <div id="videoFloat"></div> | ||
| 32 | </fieldset> | ||
| 33 | </div> | ||
| 34 | </div> | ||
| 35 | <div id="upload" class="panel"> | ||
| 36 | <div id="upload_left"> | ||
| 37 | <div id="queueList" class="queueList"> | ||
| 38 | <div class="statusBar element-invisible"> | ||
| 39 | <div class="progress"> | ||
| 40 | <span class="text">0%</span> | ||
| 41 | <span class="percentage"></span> | ||
| 42 | </div><div class="info"></div> | ||
| 43 | <div class="btns"> | ||
| 44 | <div id="filePickerBtn"></div> | ||
| 45 | <div class="uploadBtn"><var id="lang_start_upload"></var></div> | ||
| 46 | </div> | ||
| 47 | </div> | ||
| 48 | <div id="dndArea" class="placeholder"> | ||
| 49 | <div class="filePickerContainer"> | ||
| 50 | <div id="filePickerReady"></div> | ||
| 51 | </div> | ||
| 52 | </div> | ||
| 53 | <ul class="filelist element-invisible"> | ||
| 54 | <li id="filePickerBlock" class="filePickerBlock"></li> | ||
| 55 | </ul> | ||
| 56 | </div> | ||
| 57 | </div> | ||
| 58 | <div id="uploadVideoInfo"> | ||
| 59 | <fieldset> | ||
| 60 | <legend><var id="lang_upload_size"></var></legend> | ||
| 61 | <table> | ||
| 62 | <tr><td><label><var id="lang_upload_width"></var></label></td><td><input class="txt" id="upload_width" type="text"/></td></tr> | ||
| 63 | <tr><td><label><var id="lang_upload_height"></var></label></td><td><input class="txt" id="upload_height" type="text"/></td></tr> | ||
| 64 | </table> | ||
| 65 | </fieldset> | ||
| 66 | <fieldset> | ||
| 67 | <legend><var id="lang_upload_alignment"></var></legend> | ||
| 68 | <div id="upload_alignment"></div> | ||
| 69 | </fieldset> | ||
| 70 | </div> | ||
| 71 | </div> | ||
| 72 | </div> | ||
| 73 | </div> | ||
| 74 | </div> | ||
| 75 | |||
| 76 | <!-- jquery --> | ||
| 77 | <script type="text/javascript" src="../../third-party/jquery-1.10.2.min.js"></script> | ||
| 78 | |||
| 79 | <!-- webuploader --> | ||
| 80 | <script type="text/javascript" src="../../third-party/webuploader/webuploader.min.js"></script> | ||
| 81 | <link rel="stylesheet" type="text/css" href="../../third-party/webuploader/webuploader.css"> | ||
| 82 | |||
| 83 | <!-- video --> | ||
| 84 | <script type="text/javascript" src="video.js"></script> | ||
| 85 | </body> | ||
| 86 | </html> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <!DOCTYPE> | ||
| 2 | <html> | ||
| 3 | <head> | ||
| 4 | <title></title> | ||
| 5 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | ||
| 6 | <script type="text/javascript" src="../internal.js"></script> | ||
| 7 | <style type="text/css"> | ||
| 8 | .wrapper{width: 540px; margin: 10px auto;} | ||
| 9 | #appShow {border: 1px solid #ddd;} | ||
| 10 | .errorMsg{font-size: 13px;margin: 10px;color: #dd0000} | ||
| 11 | </style> | ||
| 12 | </head> | ||
| 13 | <body> | ||
| 14 | <div class="wrapper"> | ||
| 15 | <div id="appShow"></div> | ||
| 16 | </div> | ||
| 17 | <script type="text/javascript"> | ||
| 18 | //此处配置您在百度上申请到的appkey。 | ||
| 19 | var apikey = editor.options.webAppKey; | ||
| 20 | if ( apikey && apikey.length == 24 ) { | ||
| 21 | var searchConfig = { | ||
| 22 | container:'appShow', //容器ID | ||
| 23 | tips:"", //该值用于自动清空 | ||
| 24 | search:1, //是否显示搜索框 | ||
| 25 | ps:12, //每页显示的条数 | ||
| 26 | suggest:1, //是否开启搜索自动完成 | ||
| 27 | limit:0, //搜索结果显示条数,0表示无限制 | ||
| 28 | searchNow:0, //是否在初始化完成时立即搜索 | ||
| 29 | apikey:apikey, //每人得 | ||
| 30 | pager:1, | ||
| 31 | cid:7134562, | ||
| 32 | outputHTML:1 | ||
| 33 | },baiduApp; | ||
| 34 | |||
| 35 | function clickCallback() { | ||
| 36 | baiduApp.addEventListener( 'getAppHTML', function ( e, data ) { | ||
| 37 | var url = 'http://app.baidu.com/app/enter?appid='+data.data['app_id'] +'&tn=app_canvas&app_spce_id=1&apikey='+apikey+'&api_key=' + apikey; | ||
| 38 | editor.execCommand( "webapp", {url:url,width:data.uniWidth,height:data.uniHeight+60,logo:data.data['app_logo'],title:data.data['app_name']}); | ||
| 39 | dialog.close(); | ||
| 40 | } ); | ||
| 41 | } | ||
| 42 | |||
| 43 | var script = document.createElement( "script" ); | ||
| 44 | script.type = "text/javascript"; | ||
| 45 | script.src = "http://app.baidu.com/appweb/api/search?auto=yes&container=container&apikey=" + apikey + "&instanceName=baiduApp&callback=clickCallback&config=searchConfig"; | ||
| 46 | document.body.appendChild( script ); | ||
| 47 | } else { | ||
| 48 | $G( "appShow" ).innerHTML = "<p class='errorMsg'>"+lang.tip1+"<a title='"+lang.anthorApi+"' href='http://app.baidu.com/static/cms/getapikey.html' target='_blank'>"+lang.applyFor+"</a></p><p class='errorMsg'>"+lang.tip2+"</p>" ; | ||
| 49 | } | ||
| 50 | |||
| 51 | </script> | ||
| 52 | </body> | ||
| 53 | </html> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | ||
| 2 | "http://www.w3.org/TR/html4/loose.dtd"> | ||
| 3 | <html> | ||
| 4 | <head> | ||
| 5 | <title></title> | ||
| 6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | ||
| 7 | <script type="text/javascript" src="../internal.js"></script> | ||
| 8 | <style type="text/css"> | ||
| 9 | .wrapper{width: 600px;padding: 10px;height: 352px;overflow: hidden;position: relative;border-bottom: 1px solid #d7d7d7} | ||
| 10 | .localPath input{float: left;width: 350px;line-height: 20px;height: 20px;} | ||
| 11 | #clipboard{float:left;width: 70px;height: 30px; } | ||
| 12 | .description{ color: #0066cc; margin-top: 2px; width: 450px; height: 45px;float: left;line-height: 22px} | ||
| 13 | #upload{width: 100px;height: 30px;float: right; margin:10px 2px 0 0;cursor: pointer;} | ||
| 14 | #msg{ width: 140px; height: 30px; line-height:25px;float: left;color: red} | ||
| 15 | </style> | ||
| 16 | </head> | ||
| 17 | <body> | ||
| 18 | <div class="wrapper"> | ||
| 19 | <div class="localPath"> | ||
| 20 | <input id="localPath" type="text" readonly /> | ||
| 21 | <div id="clipboard"></div> | ||
| 22 | <div id="msg"></div> | ||
| 23 | </div> | ||
| 24 | <div id="flashContainer"></div> | ||
| 25 | <div> | ||
| 26 | <div id="upload" style="display: none" ><img id="uploadBtn"></div> | ||
| 27 | <div class="description"> | ||
| 28 | <span style="color: red"><var id="lang_resave"></var>: </span><var id="lang_step"></var> | ||
| 29 | </div> | ||
| 30 | </div> | ||
| 31 | </div> | ||
| 32 | <script type="text/javascript" src="tangram.js"></script> | ||
| 33 | <script type="text/javascript" src="wordimage.js"></script> | ||
| 34 | <script type="text/javascript"> | ||
| 35 | editor.setOpt({ | ||
| 36 | wordImageFieldName:"upfile", | ||
| 37 | compressSide:0, | ||
| 38 | maxImageSideLength:900 | ||
| 39 | }); | ||
| 40 | |||
| 41 | //全局变量 | ||
| 42 | var imageUrls = [], //用于保存从服务器返回的图片信息数组 | ||
| 43 | selectedImageCount = 0, //当前已选择的但未上传的图片数量 | ||
| 44 | optImageUrl = editor.getActionUrl(editor.getOpt('imageActionName')), | ||
| 45 | optImageFieldName = editor.getOpt('imageFieldName'), | ||
| 46 | optImageCompressBorder = editor.getOpt('imageCompressEnable') ? editor.getOpt('imageCompressBorder'):null, | ||
| 47 | maxSize = editor.getOpt('imageMaxSize') / 1024, | ||
| 48 | extension = editor.getOpt('imageAllowFiles').join(';').replace(/\./g, '*.'); | ||
| 49 | |||
| 50 | /* 添加额外的GET参数 */ | ||
| 51 | var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '', | ||
| 52 | urlWidthParams = optImageUrl + (optImageUrl.indexOf('?') == -1 ? '?':'&') + params; | ||
| 53 | |||
| 54 | utils.domReady(function(){ | ||
| 55 | //创建Flash相关的参数集合 | ||
| 56 | var flashOptions = { | ||
| 57 | container:"flashContainer", //flash容器id | ||
| 58 | url:urlWidthParams, // 上传处理页面的url地址 | ||
| 59 | ext:editor.queryCommandValue('serverParam') || {}, //可向服务器提交的自定义参数列表 | ||
| 60 | fileType:'{"description":"'+lang.fileType+'", "extension":"' + extension + '"}', //上传文件格式限制 | ||
| 61 | flashUrl:'imageUploader.swf', //上传用的flash组件地址 | ||
| 62 | width:600, //flash的宽度 | ||
| 63 | height:272, //flash的高度 | ||
| 64 | gridWidth:120, // 每一个预览图片所占的宽度 | ||
| 65 | gridHeight:120, // 每一个预览图片所占的高度 | ||
| 66 | picWidth:100, // 单张预览图片的宽度 | ||
| 67 | picHeight:100, // 单张预览图片的高度 | ||
| 68 | uploadDataFieldName: optImageFieldName, // POST请求中图片数据的key | ||
| 69 | picDescFieldName:'pictitle', // POST请求中图片描述的key | ||
| 70 | maxSize: maxSize, // 文件的最大体积,单位M | ||
| 71 | compressSize:1, // 上传前如果图片体积超过该值,会先压缩,单位M | ||
| 72 | maxNum:32, // 单次最大可上传多少个文件 | ||
| 73 | compressSide: 0, //等比压缩的基准,0为按照最长边,1为按照宽度,2为按照高度 | ||
| 74 | compressLength: optImageCompressBorder //能接受的最大边长,超过该值Flash会自动等比压缩 | ||
| 75 | }; | ||
| 76 | //回调函数集合,支持传递函数名的字符串、函数句柄以及函数本身三种类型 | ||
| 77 | var callbacks={ | ||
| 78 | selectFileCallback: function(selectFiles){ // 选择文件的回调 | ||
| 79 | selectedImageCount += selectFiles.length; | ||
| 80 | if(selectedImageCount) baidu.g("upload").style.display = ""; | ||
| 81 | dialog.buttons[0].setDisabled(true); //初始化时置灰确定按钮 | ||
| 82 | }, | ||
| 83 | deleteFileCallback: function(delFiles){ // 删除文件的回调 | ||
| 84 | selectedImageCount -= delFiles.length; | ||
| 85 | if (!selectedImageCount) { | ||
| 86 | baidu.g("upload").style.display = "none"; | ||
| 87 | dialog.buttons[0].setDisabled(false); //没有选择图片时重新点亮按钮 | ||
| 88 | } | ||
| 89 | }, | ||
| 90 | uploadCompleteCallback: function(data){ // 单个文件上传完成的回调 | ||
| 91 | try{var info = eval("(" + data.info + ")"); | ||
| 92 | info && imageUrls.push(info); | ||
| 93 | selectedImageCount--; | ||
| 94 | }catch(e){} | ||
| 95 | }, | ||
| 96 | uploadErrorCallback: function (data){ // 单个文件上传失败的回调, | ||
| 97 | console && console.log(data); | ||
| 98 | }, | ||
| 99 | allCompleteCallback: function(){ // 全部上传完成时的回调 | ||
| 100 | dialog.buttons[0].setDisabled(false); //上传完毕后点亮按钮 | ||
| 101 | } | ||
| 102 | //exceedFileCallback: 'exceedFileCallback', // 文件超出限制的最大体积时的回调 | ||
| 103 | //startUploadCallback: startUploadCallback // 开始上传某个文件时的回调 | ||
| 104 | }; | ||
| 105 | wordImage.init(flashOptions,callbacks); | ||
| 106 | }); | ||
| 107 | |||
| 108 | </script> | ||
| 109 | |||
| 110 | </body> | ||
| 111 | </html> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | /** | ||
| 2 | * Created by JetBrains PhpStorm. | ||
| 3 | * User: taoqili | ||
| 4 | * Date: 12-1-30 | ||
| 5 | * Time: 下午12:50 | ||
| 6 | * To change this template use File | Settings | File Templates. | ||
| 7 | */ | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | var wordImage = {}; | ||
| 12 | //(function(){ | ||
| 13 | var g = baidu.g, | ||
| 14 | flashObj,flashContainer; | ||
| 15 | |||
| 16 | wordImage.init = function(opt, callbacks) { | ||
| 17 | showLocalPath("localPath"); | ||
| 18 | //createCopyButton("clipboard","localPath"); | ||
| 19 | createFlashUploader(opt, callbacks); | ||
| 20 | addUploadListener(); | ||
| 21 | addOkListener(); | ||
| 22 | }; | ||
| 23 | |||
| 24 | function hideFlash(){ | ||
| 25 | flashObj = null; | ||
| 26 | flashContainer.innerHTML = ""; | ||
| 27 | } | ||
| 28 | function addOkListener() { | ||
| 29 | dialog.onok = function() { | ||
| 30 | if (!imageUrls.length) return; | ||
| 31 | var urlPrefix = editor.getOpt('imageUrlPrefix'), | ||
| 32 | images = domUtils.getElementsByTagName(editor.document,"img"); | ||
| 33 | editor.fireEvent('saveScene'); | ||
| 34 | for (var i = 0,img; img = images[i++];) { | ||
| 35 | var src = img.getAttribute("word_img"); | ||
| 36 | if (!src) continue; | ||
| 37 | for (var j = 0,url; url = imageUrls[j++];) { | ||
| 38 | if (src.indexOf(url.original.replace(" ","")) != -1) { | ||
| 39 | img.src = urlPrefix + url.url; | ||
| 40 | img.setAttribute("_src", urlPrefix + url.url); //同时修改"_src"属性 | ||
| 41 | img.setAttribute("title",url.title); | ||
| 42 | domUtils.removeAttributes(img, ["word_img","style","width","height"]); | ||
| 43 | editor.fireEvent("selectionchange"); | ||
| 44 | break; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | editor.fireEvent('saveScene'); | ||
| 49 | hideFlash(); | ||
| 50 | }; | ||
| 51 | dialog.oncancel = function(){ | ||
| 52 | hideFlash(); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * 绑定开始上传事件 | ||
| 58 | */ | ||
| 59 | function addUploadListener() { | ||
| 60 | g("upload").onclick = function () { | ||
| 61 | flashObj.upload(); | ||
| 62 | this.style.display = "none"; | ||
| 63 | }; | ||
| 64 | } | ||
| 65 | |||
| 66 | function showLocalPath(id) { | ||
| 67 | //单张编辑 | ||
| 68 | var img = editor.selection.getRange().getClosedNode(); | ||
| 69 | var images = editor.execCommand('wordimage'); | ||
| 70 | if(images.length==1 || img && img.tagName == 'IMG'){ | ||
| 71 | g(id).value = images[0]; | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | var path = images[0]; | ||
| 75 | var leftSlashIndex = path.lastIndexOf("/")||0, //不同版本的doc和浏览器都可能影响到这个符号,故直接判断两种 | ||
| 76 | rightSlashIndex = path.lastIndexOf("\\")||0, | ||
| 77 | separater = leftSlashIndex > rightSlashIndex ? "/":"\\" ; | ||
| 78 | |||
| 79 | path = path.substring(0, path.lastIndexOf(separater)+1); | ||
| 80 | g(id).value = path; | ||
| 81 | } | ||
| 82 | |||
| 83 | function createFlashUploader(opt, callbacks) { | ||
| 84 | //由于lang.flashI18n是静态属性,不可以直接进行修改,否则会影响到后续内容 | ||
| 85 | var i18n = utils.extend({},lang.flashI18n); | ||
| 86 | //处理图片资源地址的编码,补全等问题 | ||
| 87 | for(var i in i18n){ | ||
| 88 | if(!(i in {"lang":1,"uploadingTF":1,"imageTF":1,"textEncoding":1}) && i18n[i]){ | ||
| 89 | i18n[i] = encodeURIComponent(editor.options.langPath + editor.options.lang + "/images/" + i18n[i]); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | opt = utils.extend(opt,i18n,false); | ||
| 93 | var option = { | ||
| 94 | createOptions:{ | ||
| 95 | id:'flash', | ||
| 96 | url:opt.flashUrl, | ||
| 97 | width:opt.width, | ||
| 98 | height:opt.height, | ||
| 99 | errorMessage:lang.flashError, | ||
| 100 | wmode:browser.safari ? 'transparent' : 'window', | ||
| 101 | ver:'10.0.0', | ||
| 102 | vars:opt, | ||
| 103 | container:opt.container | ||
| 104 | } | ||
| 105 | }; | ||
| 106 | |||
| 107 | option = extendProperty(callbacks, option); | ||
| 108 | flashObj = new baidu.flash.imageUploader(option); | ||
| 109 | flashContainer = $G(opt.container); | ||
| 110 | } | ||
| 111 | |||
| 112 | function extendProperty(fromObj, toObj) { | ||
| 113 | for (var i in fromObj) { | ||
| 114 | if (!toObj[i]) { | ||
| 115 | toObj[i] = fromObj[i]; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | return toObj; | ||
| 119 | } | ||
| 120 | |||
| 121 | //})(); | ||
| 122 | |||
| 123 | function getPasteData(id) { | ||
| 124 | baidu.g("msg").innerHTML = lang.copySuccess + "</br>"; | ||
| 125 | setTimeout(function() { | ||
| 126 | baidu.g("msg").innerHTML = ""; | ||
| 127 | }, 5000); | ||
| 128 | return baidu.g(id).value; | ||
| 129 | } | ||
| 130 | |||
| 131 | function createCopyButton(id, dataFrom) { | ||
| 132 | baidu.swf.create({ | ||
| 133 | id:"copyFlash", | ||
| 134 | url:"fClipboard_ueditor.swf", | ||
| 135 | width:"58", | ||
| 136 | height:"25", | ||
| 137 | errorMessage:"", | ||
| 138 | bgColor:"#CBCBCB", | ||
| 139 | wmode:"transparent", | ||
| 140 | ver:"10.0.0", | ||
| 141 | vars:{ | ||
| 142 | tid:dataFrom | ||
| 143 | } | ||
| 144 | }, id | ||
| 145 | ); | ||
| 146 | |||
| 147 | var clipboard = baidu.swf.getMovie("copyFlash"); | ||
| 148 | var clipinterval = setInterval(function() { | ||
| 149 | if (clipboard && clipboard.flashInit) { | ||
| 150 | clearInterval(clipinterval); | ||
| 151 | clipboard.setHandCursor(true); | ||
| 152 | clipboard.setContentFuncName("getPasteData"); | ||
| 153 | //clipboard.setMEFuncName("mouseEventHandler"); | ||
| 154 | } | ||
| 155 | }, 500); | ||
| 156 | } | ||
| 157 | createCopyButton("clipboard", "localPath"); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | ||
| 2 | "http://www.w3.org/TR/html4/loose.dtd"> | ||
| 3 | <html> | ||
| 4 | <head> | ||
| 5 | <title>完整demo</title> | ||
| 6 | <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | ||
| 7 | <script type="text/javascript" charset="utf-8" src="ueditor.config.js"></script> | ||
| 8 | <script type="text/javascript" charset="utf-8" src="ueditor.all.min.js"> </script> | ||
| 9 | <!--建议手动加在语言,避免在ie下有时因为加载语言失败导致编辑器加载失败--> | ||
| 10 | <!--这里加载的语言文件会覆盖你在配置项目里添加的语言类型,比如你在配置项目里配置的是英文,这里加载的中文,那最后就是中文--> | ||
| 11 | <script type="text/javascript" charset="utf-8" src="lang/zh-cn/zh-cn.js"></script> | ||
| 12 | |||
| 13 | <style type="text/css"> | ||
| 14 | div{ | ||
| 15 | width:100%; | ||
| 16 | } | ||
| 17 | </style> | ||
| 18 | </head> | ||
| 19 | <body> | ||
| 20 | <div> | ||
| 21 | <h1>完整demo</h1> | ||
| 22 | <script id="editor" type="text/plain" style="width:1024px;height:500px;"></script> | ||
| 23 | </div> | ||
| 24 | <div id="btns"> | ||
| 25 | <div> | ||
| 26 | <button onclick="getAllHtml()">获得整个html的内容</button> | ||
| 27 | <button onclick="getContent()">获得内容</button> | ||
| 28 | <button onclick="setContent()">写入内容</button> | ||
| 29 | <button onclick="setContent(true)">追加内容</button> | ||
| 30 | <button onclick="getContentTxt()">获得纯文本</button> | ||
| 31 | <button onclick="getPlainTxt()">获得带格式的纯文本</button> | ||
| 32 | <button onclick="hasContent()">判断是否有内容</button> | ||
| 33 | <button onclick="setFocus()">使编辑器获得焦点</button> | ||
| 34 | <button onmousedown="isFocus(event)">编辑器是否获得焦点</button> | ||
| 35 | <button onmousedown="setblur(event)" >编辑器失去焦点</button> | ||
| 36 | |||
| 37 | </div> | ||
| 38 | <div> | ||
| 39 | <button onclick="getText()">获得当前选中的文本</button> | ||
| 40 | <button onclick="insertHtml()">插入给定的内容</button> | ||
| 41 | <button id="enable" onclick="setEnabled()">可以编辑</button> | ||
| 42 | <button onclick="setDisabled()">不可编辑</button> | ||
| 43 | <button onclick=" UE.getEditor('editor').setHide()">隐藏编辑器</button> | ||
| 44 | <button onclick=" UE.getEditor('editor').setShow()">显示编辑器</button> | ||
| 45 | <button onclick=" UE.getEditor('editor').setHeight(300)">设置高度为300默认关闭了自动长高</button> | ||
| 46 | </div> | ||
| 47 | |||
| 48 | <div> | ||
| 49 | <button onclick="getLocalData()" >获取草稿箱内容</button> | ||
| 50 | <button onclick="clearLocalData()" >清空草稿箱</button> | ||
| 51 | </div> | ||
| 52 | |||
| 53 | </div> | ||
| 54 | <div> | ||
| 55 | <button onclick="createEditor()"> | ||
| 56 | 创建编辑器</button> | ||
| 57 | <button onclick="deleteEditor()"> | ||
| 58 | 删除编辑器</button> | ||
| 59 | </div> | ||
| 60 | |||
| 61 | <script type="text/javascript"> | ||
| 62 | |||
| 63 | //实例化编辑器 | ||
| 64 | //建议使用工厂方法getEditor创建和引用编辑器实例,如果在某个闭包下引用该编辑器,直接调用UE.getEditor('editor')就能拿到相关的实例 | ||
| 65 | var ue = UE.getEditor('editor'); | ||
| 66 | |||
| 67 | |||
| 68 | function isFocus(e){ | ||
| 69 | alert(UE.getEditor('editor').isFocus()); | ||
| 70 | UE.dom.domUtils.preventDefault(e) | ||
| 71 | } | ||
| 72 | function setblur(e){ | ||
| 73 | UE.getEditor('editor').blur(); | ||
| 74 | UE.dom.domUtils.preventDefault(e) | ||
| 75 | } | ||
| 76 | function insertHtml() { | ||
| 77 | var value = prompt('插入html代码', ''); | ||
| 78 | UE.getEditor('editor').execCommand('insertHtml', value) | ||
| 79 | } | ||
| 80 | function createEditor() { | ||
| 81 | enableBtn(); | ||
| 82 | UE.getEditor('editor'); | ||
| 83 | } | ||
| 84 | function getAllHtml() { | ||
| 85 | alert(UE.getEditor('editor').getAllHtml()) | ||
| 86 | } | ||
| 87 | function getContent() { | ||
| 88 | var arr = []; | ||
| 89 | arr.push("使用editor.getContent()方法可以获得编辑器的内容"); | ||
| 90 | arr.push("内容为:"); | ||
| 91 | arr.push(UE.getEditor('editor').getContent()); | ||
| 92 | alert(arr.join("\n")); | ||
| 93 | } | ||
| 94 | function getPlainTxt() { | ||
| 95 | var arr = []; | ||
| 96 | arr.push("使用editor.getPlainTxt()方法可以获得编辑器的带格式的纯文本内容"); | ||
| 97 | arr.push("内容为:"); | ||
| 98 | arr.push(UE.getEditor('editor').getPlainTxt()); | ||
| 99 | alert(arr.join('\n')) | ||
| 100 | } | ||
| 101 | function setContent(isAppendTo) { | ||
| 102 | var arr = []; | ||
| 103 | arr.push("使用editor.setContent('欢迎使用ueditor')方法可以设置编辑器的内容"); | ||
| 104 | UE.getEditor('editor').setContent('欢迎使用ueditor', isAppendTo); | ||
| 105 | alert(arr.join("\n")); | ||
| 106 | } | ||
| 107 | function setDisabled() { | ||
| 108 | UE.getEditor('editor').setDisabled('fullscreen'); | ||
| 109 | disableBtn("enable"); | ||
| 110 | } | ||
| 111 | |||
| 112 | function setEnabled() { | ||
| 113 | UE.getEditor('editor').setEnabled(); | ||
| 114 | enableBtn(); | ||
| 115 | } | ||
| 116 | |||
| 117 | function getText() { | ||
| 118 | //当你点击按钮时编辑区域已经失去了焦点,如果直接用getText将不会得到内容,所以要在选回来,然后取得内容 | ||
| 119 | var range = UE.getEditor('editor').selection.getRange(); | ||
| 120 | range.select(); | ||
| 121 | var txt = UE.getEditor('editor').selection.getText(); | ||
| 122 | alert(txt) | ||
| 123 | } | ||
| 124 | |||
| 125 | function getContentTxt() { | ||
| 126 | var arr = []; | ||
| 127 | arr.push("使用editor.getContentTxt()方法可以获得编辑器的纯文本内容"); | ||
| 128 | arr.push("编辑器的纯文本内容为:"); | ||
| 129 | arr.push(UE.getEditor('editor').getContentTxt()); | ||
| 130 | alert(arr.join("\n")); | ||
| 131 | } | ||
| 132 | function hasContent() { | ||
| 133 | var arr = []; | ||
| 134 | arr.push("使用editor.hasContents()方法判断编辑器里是否有内容"); | ||
| 135 | arr.push("判断结果为:"); | ||
| 136 | arr.push(UE.getEditor('editor').hasContents()); | ||
| 137 | alert(arr.join("\n")); | ||
| 138 | } | ||
| 139 | function setFocus() { | ||
| 140 | UE.getEditor('editor').focus(); | ||
| 141 | } | ||
| 142 | function deleteEditor() { | ||
| 143 | disableBtn(); | ||
| 144 | UE.getEditor('editor').destroy(); | ||
| 145 | } | ||
| 146 | function disableBtn(str) { | ||
| 147 | var div = document.getElementById('btns'); | ||
| 148 | var btns = UE.dom.domUtils.getElementsByTagName(div, "button"); | ||
| 149 | for (var i = 0, btn; btn = btns[i++];) { | ||
| 150 | if (btn.id == str) { | ||
| 151 | UE.dom.domUtils.removeAttributes(btn, ["disabled"]); | ||
| 152 | } else { | ||
| 153 | btn.setAttribute("disabled", "true"); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | function enableBtn() { | ||
| 158 | var div = document.getElementById('btns'); | ||
| 159 | var btns = UE.dom.domUtils.getElementsByTagName(div, "button"); | ||
| 160 | for (var i = 0, btn; btn = btns[i++];) { | ||
| 161 | UE.dom.domUtils.removeAttributes(btn, ["disabled"]); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | function getLocalData () { | ||
| 166 | alert(UE.getEditor('editor').execCommand( "getlocaldata" )); | ||
| 167 | } | ||
| 168 | |||
| 169 | function clearLocalData () { | ||
| 170 | UE.getEditor('editor').execCommand( "clearlocaldata" ); | ||
| 171 | alert("已清空草稿箱") | ||
| 172 | } | ||
| 173 | </script> | ||
| 174 | </body> | ||
| 175 | </html> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | /* 前后端通信相关的配置,注释只允许使用多行方式 */ | ||
| 2 | { | ||
| 3 | /* 上传图片配置项 */ | ||
| 4 | "imageActionName": "uploadimage", /* 执行上传图片的action名称 */ | ||
| 5 | "imageFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 6 | "imageMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 7 | "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */ | ||
| 8 | "imageCompressEnable": true, /* 是否压缩图片,默认是true */ | ||
| 9 | "imageCompressBorder": 1600, /* 图片压缩最长边限制 */ | ||
| 10 | "imageInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 11 | "imageUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 12 | "imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 13 | /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */ | ||
| 14 | /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */ | ||
| 15 | /* {time} 会替换成时间戳 */ | ||
| 16 | /* {yyyy} 会替换成四位年份 */ | ||
| 17 | /* {yy} 会替换成两位年份 */ | ||
| 18 | /* {mm} 会替换成两位月份 */ | ||
| 19 | /* {dd} 会替换成两位日期 */ | ||
| 20 | /* {hh} 会替换成两位小时 */ | ||
| 21 | /* {ii} 会替换成两位分钟 */ | ||
| 22 | /* {ss} 会替换成两位秒 */ | ||
| 23 | /* 非法字符 \ : * ? " < > | */ | ||
| 24 | /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */ | ||
| 25 | |||
| 26 | /* 涂鸦图片上传配置项 */ | ||
| 27 | "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */ | ||
| 28 | "scrawlFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 29 | "scrawlPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 30 | "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 31 | "scrawlUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 32 | "scrawlInsertAlign": "none", | ||
| 33 | |||
| 34 | /* 截图工具上传 */ | ||
| 35 | "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */ | ||
| 36 | "snapscreenPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 37 | "snapscreenUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 38 | "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 39 | |||
| 40 | /* 抓取远程图片配置 */ | ||
| 41 | "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], | ||
| 42 | "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */ | ||
| 43 | "catcherFieldName": "source", /* 提交的图片列表表单名称 */ | ||
| 44 | "catcherPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 45 | "catcherUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 46 | "catcherMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 47 | "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */ | ||
| 48 | |||
| 49 | /* 上传视频配置 */ | ||
| 50 | "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */ | ||
| 51 | "videoFieldName": "upfile", /* 提交的视频表单名称 */ | ||
| 52 | "videoPathFormat": "/ueditor/jsp/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 53 | "videoUrlPrefix": "", /* 视频访问路径前缀 */ | ||
| 54 | "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */ | ||
| 55 | "videoAllowFiles": [ | ||
| 56 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 57 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */ | ||
| 58 | |||
| 59 | /* 上传文件配置 */ | ||
| 60 | "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */ | ||
| 61 | "fileFieldName": "upfile", /* 提交的文件表单名称 */ | ||
| 62 | "filePathFormat": "/ueditor/jsp/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 63 | "fileUrlPrefix": "", /* 文件访问路径前缀 */ | ||
| 64 | "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */ | ||
| 65 | "fileAllowFiles": [ | ||
| 66 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 67 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 68 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 69 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 70 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 71 | ], /* 上传文件格式显示 */ | ||
| 72 | |||
| 73 | /* 列出指定目录下的图片 */ | ||
| 74 | "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */ | ||
| 75 | "imageManagerListPath": "/ueditor/jsp/upload/image/", /* 指定要列出图片的目录 */ | ||
| 76 | "imageManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 77 | "imageManagerUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 78 | "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 79 | "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */ | ||
| 80 | |||
| 81 | /* 列出指定目录下的文件 */ | ||
| 82 | "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */ | ||
| 83 | "fileManagerListPath": "/ueditor/jsp/upload/file/", /* 指定要列出文件的目录 */ | ||
| 84 | "fileManagerUrlPrefix": "", /* 文件访问路径前缀 */ | ||
| 85 | "fileManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 86 | "fileManagerAllowFiles": [ | ||
| 87 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 88 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 89 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 90 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 91 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 92 | ] /* 列出的文件类型 */ | ||
| 93 | |||
| 94 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | package com.baidu.ueditor; | ||
| 2 | |||
| 3 | import java.util.Map; | ||
| 4 | |||
| 5 | import javax.servlet.http.HttpServletRequest; | ||
| 6 | |||
| 7 | import com.baidu.ueditor.define.ActionMap; | ||
| 8 | import com.baidu.ueditor.define.AppInfo; | ||
| 9 | import com.baidu.ueditor.define.BaseState; | ||
| 10 | import com.baidu.ueditor.define.State; | ||
| 11 | import com.baidu.ueditor.hunter.FileManager; | ||
| 12 | import com.baidu.ueditor.hunter.ImageHunter; | ||
| 13 | import com.baidu.ueditor.upload.Uploader; | ||
| 14 | |||
| 15 | public class ActionEnter { | ||
| 16 | |||
| 17 | private HttpServletRequest request = null; | ||
| 18 | |||
| 19 | private String rootPath = null; | ||
| 20 | private String contextPath = null; | ||
| 21 | |||
| 22 | private String actionType = null; | ||
| 23 | |||
| 24 | private ConfigManager configManager = null; | ||
| 25 | |||
| 26 | public ActionEnter ( HttpServletRequest request, String rootPath ) { | ||
| 27 | |||
| 28 | this.request = request; | ||
| 29 | this.rootPath = rootPath; | ||
| 30 | this.actionType = request.getParameter( "action" ); | ||
| 31 | this.contextPath = request.getContextPath(); | ||
| 32 | this.configManager = ConfigManager.getInstance( this.rootPath, this.contextPath, request.getRequestURI() ); | ||
| 33 | |||
| 34 | } | ||
| 35 | |||
| 36 | public String exec () { | ||
| 37 | |||
| 38 | String callbackName = this.request.getParameter("callback"); | ||
| 39 | |||
| 40 | if ( callbackName != null ) { | ||
| 41 | |||
| 42 | if ( !validCallbackName( callbackName ) ) { | ||
| 43 | return new BaseState( false, AppInfo.ILLEGAL ).toJSONString(); | ||
| 44 | } | ||
| 45 | |||
| 46 | return callbackName+"("+this.invoke()+");"; | ||
| 47 | |||
| 48 | } else { | ||
| 49 | return this.invoke(); | ||
| 50 | } | ||
| 51 | |||
| 52 | } | ||
| 53 | |||
| 54 | public String invoke() { | ||
| 55 | |||
| 56 | if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) { | ||
| 57 | return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString(); | ||
| 58 | } | ||
| 59 | |||
| 60 | if ( this.configManager == null || !this.configManager.valid() ) { | ||
| 61 | return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString(); | ||
| 62 | } | ||
| 63 | |||
| 64 | State state = null; | ||
| 65 | |||
| 66 | int actionCode = ActionMap.getType( this.actionType ); | ||
| 67 | |||
| 68 | Map<String, Object> conf = null; | ||
| 69 | |||
| 70 | switch ( actionCode ) { | ||
| 71 | |||
| 72 | case ActionMap.CONFIG: | ||
| 73 | return this.configManager.getAllConfig().toString(); | ||
| 74 | |||
| 75 | case ActionMap.UPLOAD_IMAGE: | ||
| 76 | case ActionMap.UPLOAD_SCRAWL: | ||
| 77 | case ActionMap.UPLOAD_VIDEO: | ||
| 78 | case ActionMap.UPLOAD_FILE: | ||
| 79 | conf = this.configManager.getConfig( actionCode ); | ||
| 80 | state = new Uploader( request, conf ).doExec(); | ||
| 81 | break; | ||
| 82 | |||
| 83 | case ActionMap.CATCH_IMAGE: | ||
| 84 | conf = configManager.getConfig( actionCode ); | ||
| 85 | String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) ); | ||
| 86 | state = new ImageHunter( conf ).capture( list ); | ||
| 87 | break; | ||
| 88 | |||
| 89 | case ActionMap.LIST_IMAGE: | ||
| 90 | case ActionMap.LIST_FILE: | ||
| 91 | conf = configManager.getConfig( actionCode ); | ||
| 92 | int start = this.getStartIndex(); | ||
| 93 | state = new FileManager( conf ).listFile( start ); | ||
| 94 | break; | ||
| 95 | |||
| 96 | } | ||
| 97 | |||
| 98 | return state.toJSONString(); | ||
| 99 | |||
| 100 | } | ||
| 101 | |||
| 102 | public int getStartIndex () { | ||
| 103 | |||
| 104 | String start = this.request.getParameter( "start" ); | ||
| 105 | |||
| 106 | try { | ||
| 107 | return Integer.parseInt( start ); | ||
| 108 | } catch ( Exception e ) { | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * callback参数验证 | ||
| 116 | */ | ||
| 117 | public boolean validCallbackName ( String name ) { | ||
| 118 | |||
| 119 | if ( name.matches( "^[a-zA-Z_]+[\\w0-9_]*$" ) ) { | ||
| 120 | return true; | ||
| 121 | } | ||
| 122 | |||
| 123 | return false; | ||
| 124 | |||
| 125 | } | ||
| 126 | |||
| 127 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | package com.baidu.ueditor; | ||
| 2 | |||
| 3 | import java.io.BufferedReader; | ||
| 4 | import java.io.File; | ||
| 5 | import java.io.FileInputStream; | ||
| 6 | import java.io.FileNotFoundException; | ||
| 7 | import java.io.IOException; | ||
| 8 | import java.io.InputStreamReader; | ||
| 9 | import java.io.UnsupportedEncodingException; | ||
| 10 | import java.util.HashMap; | ||
| 11 | import java.util.Map; | ||
| 12 | |||
| 13 | import org.json.JSONArray; | ||
| 14 | import org.json.JSONObject; | ||
| 15 | |||
| 16 | import com.baidu.ueditor.define.ActionMap; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * 配置管理器 | ||
| 20 | * @author hancong03@baidu.com | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | public final class ConfigManager { | ||
| 24 | |||
| 25 | private final String rootPath; | ||
| 26 | private final String originalPath; | ||
| 27 | private final String contextPath; | ||
| 28 | private static final String configFileName = "config.json"; | ||
| 29 | private String parentPath = null; | ||
| 30 | private JSONObject jsonConfig = null; | ||
| 31 | // 涂鸦上传filename定义 | ||
| 32 | private final static String SCRAWL_FILE_NAME = "scrawl"; | ||
| 33 | // 远程图片抓取filename定义 | ||
| 34 | private final static String REMOTE_FILE_NAME = "remote"; | ||
| 35 | |||
| 36 | /* | ||
| 37 | * 通过一个给定的路径构建一个配置管理器, 该管理器要求地址路径所在目录下必须存在config.properties文件 | ||
| 38 | */ | ||
| 39 | private ConfigManager ( String rootPath, String contextPath, String uri ) throws FileNotFoundException, IOException { | ||
| 40 | |||
| 41 | rootPath = rootPath.replace( "\\", "/" ); | ||
| 42 | |||
| 43 | this.rootPath = rootPath; | ||
| 44 | this.contextPath = contextPath; | ||
| 45 | |||
| 46 | if ( contextPath.length() > 0 ) { | ||
| 47 | this.originalPath = this.rootPath + uri.substring( contextPath.length() ); | ||
| 48 | } else { | ||
| 49 | this.originalPath = this.rootPath + uri; | ||
| 50 | } | ||
| 51 | |||
| 52 | this.initEnv(); | ||
| 53 | |||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * 配置管理器构造工厂 | ||
| 58 | * @param rootPath 服务器根路径 | ||
| 59 | * @param contextPath 服务器所在项目路径 | ||
| 60 | * @param uri 当前访问的uri | ||
| 61 | * @return 配置管理器实例或者null | ||
| 62 | */ | ||
| 63 | public static ConfigManager getInstance ( String rootPath, String contextPath, String uri ) { | ||
| 64 | |||
| 65 | try { | ||
| 66 | return new ConfigManager(rootPath, contextPath, uri); | ||
| 67 | } catch ( Exception e ) { | ||
| 68 | return null; | ||
| 69 | } | ||
| 70 | |||
| 71 | } | ||
| 72 | |||
| 73 | // 验证配置文件加载是否正确 | ||
| 74 | public boolean valid () { | ||
| 75 | return this.jsonConfig != null; | ||
| 76 | } | ||
| 77 | |||
| 78 | public JSONObject getAllConfig () { | ||
| 79 | |||
| 80 | return this.jsonConfig; | ||
| 81 | |||
| 82 | } | ||
| 83 | |||
| 84 | public Map<String, Object> getConfig ( int type ) { | ||
| 85 | |||
| 86 | Map<String, Object> conf = new HashMap<String, Object>(); | ||
| 87 | String savePath = null; | ||
| 88 | |||
| 89 | switch ( type ) { | ||
| 90 | |||
| 91 | case ActionMap.UPLOAD_FILE: | ||
| 92 | conf.put( "isBase64", "false" ); | ||
| 93 | conf.put( "maxSize", this.jsonConfig.getLong( "fileMaxSize" ) ); | ||
| 94 | conf.put( "allowFiles", this.getArray( "fileAllowFiles" ) ); | ||
| 95 | conf.put( "fieldName", this.jsonConfig.getString( "fileFieldName" ) ); | ||
| 96 | savePath = this.jsonConfig.getString( "filePathFormat" ); | ||
| 97 | break; | ||
| 98 | |||
| 99 | case ActionMap.UPLOAD_IMAGE: | ||
| 100 | conf.put( "isBase64", "false" ); | ||
| 101 | conf.put( "maxSize", this.jsonConfig.getLong( "imageMaxSize" ) ); | ||
| 102 | conf.put( "allowFiles", this.getArray( "imageAllowFiles" ) ); | ||
| 103 | conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) ); | ||
| 104 | savePath = this.jsonConfig.getString( "imagePathFormat" ); | ||
| 105 | break; | ||
| 106 | |||
| 107 | case ActionMap.UPLOAD_VIDEO: | ||
| 108 | conf.put( "maxSize", this.jsonConfig.getLong( "videoMaxSize" ) ); | ||
| 109 | conf.put( "allowFiles", this.getArray( "videoAllowFiles" ) ); | ||
| 110 | conf.put( "fieldName", this.jsonConfig.getString( "videoFieldName" ) ); | ||
| 111 | savePath = this.jsonConfig.getString( "videoPathFormat" ); | ||
| 112 | break; | ||
| 113 | |||
| 114 | case ActionMap.UPLOAD_SCRAWL: | ||
| 115 | conf.put( "filename", ConfigManager.SCRAWL_FILE_NAME ); | ||
| 116 | conf.put( "maxSize", this.jsonConfig.getLong( "scrawlMaxSize" ) ); | ||
| 117 | conf.put( "fieldName", this.jsonConfig.getString( "scrawlFieldName" ) ); | ||
| 118 | conf.put( "isBase64", "true" ); | ||
| 119 | savePath = this.jsonConfig.getString( "scrawlPathFormat" ); | ||
| 120 | break; | ||
| 121 | |||
| 122 | case ActionMap.CATCH_IMAGE: | ||
| 123 | conf.put( "filename", ConfigManager.REMOTE_FILE_NAME ); | ||
| 124 | conf.put( "filter", this.getArray( "catcherLocalDomain" ) ); | ||
| 125 | conf.put( "maxSize", this.jsonConfig.getLong( "catcherMaxSize" ) ); | ||
| 126 | conf.put( "allowFiles", this.getArray( "catcherAllowFiles" ) ); | ||
| 127 | conf.put( "fieldName", this.jsonConfig.getString( "catcherFieldName" ) + "[]" ); | ||
| 128 | savePath = this.jsonConfig.getString( "catcherPathFormat" ); | ||
| 129 | break; | ||
| 130 | |||
| 131 | case ActionMap.LIST_IMAGE: | ||
| 132 | conf.put( "allowFiles", this.getArray( "imageManagerAllowFiles" ) ); | ||
| 133 | conf.put( "dir", this.jsonConfig.getString( "imageManagerListPath" ) ); | ||
| 134 | conf.put( "count", this.jsonConfig.getInt( "imageManagerListSize" ) ); | ||
| 135 | break; | ||
| 136 | |||
| 137 | case ActionMap.LIST_FILE: | ||
| 138 | conf.put( "allowFiles", this.getArray( "fileManagerAllowFiles" ) ); | ||
| 139 | conf.put( "dir", this.jsonConfig.getString( "fileManagerListPath" ) ); | ||
| 140 | conf.put( "count", this.jsonConfig.getInt( "fileManagerListSize" ) ); | ||
| 141 | break; | ||
| 142 | |||
| 143 | } | ||
| 144 | |||
| 145 | conf.put( "savePath", savePath ); | ||
| 146 | conf.put( "rootPath", this.rootPath ); | ||
| 147 | |||
| 148 | return conf; | ||
| 149 | |||
| 150 | } | ||
| 151 | |||
| 152 | private void initEnv () throws FileNotFoundException, IOException { | ||
| 153 | |||
| 154 | File file = new File( this.originalPath ); | ||
| 155 | |||
| 156 | if ( !file.isAbsolute() ) { | ||
| 157 | file = new File( file.getAbsolutePath() ); | ||
| 158 | } | ||
| 159 | |||
| 160 | this.parentPath = file.getParent(); | ||
| 161 | |||
| 162 | String configContent = this.readFile( this.getConfigPath() ); | ||
| 163 | |||
| 164 | try{ | ||
| 165 | JSONObject jsonConfig = new JSONObject( configContent ); | ||
| 166 | this.jsonConfig = jsonConfig; | ||
| 167 | } catch ( Exception e ) { | ||
| 168 | this.jsonConfig = null; | ||
| 169 | } | ||
| 170 | |||
| 171 | } | ||
| 172 | |||
| 173 | private String getConfigPath () { | ||
| 174 | return this.parentPath + File.separator + ConfigManager.configFileName; | ||
| 175 | } | ||
| 176 | |||
| 177 | private String[] getArray ( String key ) { | ||
| 178 | |||
| 179 | JSONArray jsonArray = this.jsonConfig.getJSONArray( key ); | ||
| 180 | String[] result = new String[ jsonArray.length() ]; | ||
| 181 | |||
| 182 | for ( int i = 0, len = jsonArray.length(); i < len; i++ ) { | ||
| 183 | result[i] = jsonArray.getString( i ); | ||
| 184 | } | ||
| 185 | |||
| 186 | return result; | ||
| 187 | |||
| 188 | } | ||
| 189 | |||
| 190 | private String readFile ( String path ) throws IOException { | ||
| 191 | |||
| 192 | StringBuilder builder = new StringBuilder(); | ||
| 193 | |||
| 194 | try { | ||
| 195 | |||
| 196 | InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" ); | ||
| 197 | BufferedReader bfReader = new BufferedReader( reader ); | ||
| 198 | |||
| 199 | String tmpContent = null; | ||
| 200 | |||
| 201 | while ( ( tmpContent = bfReader.readLine() ) != null ) { | ||
| 202 | builder.append( tmpContent ); | ||
| 203 | } | ||
| 204 | |||
| 205 | bfReader.close(); | ||
| 206 | |||
| 207 | } catch ( UnsupportedEncodingException e ) { | ||
| 208 | // 忽略 | ||
| 209 | } | ||
| 210 | |||
| 211 | return this.filter( builder.toString() ); | ||
| 212 | |||
| 213 | } | ||
| 214 | |||
| 215 | // 过滤输入字符串, 剔除多行注释以及替换掉反斜杠 | ||
| 216 | private String filter ( String input ) { | ||
| 217 | |||
| 218 | return input.replaceAll( "/\\*[\\s\\S]*?\\*/", "" ); | ||
| 219 | |||
| 220 | } | ||
| 221 | |||
| 222 | } |
| 1 | package com.baidu.ueditor; | ||
| 2 | |||
| 3 | import java.text.SimpleDateFormat; | ||
| 4 | import java.util.Date; | ||
| 5 | import java.util.regex.Matcher; | ||
| 6 | import java.util.regex.Pattern; | ||
| 7 | |||
| 8 | public class PathFormat { | ||
| 9 | |||
| 10 | private static final String TIME = "time"; | ||
| 11 | private static final String FULL_YEAR = "yyyy"; | ||
| 12 | private static final String YEAR = "yy"; | ||
| 13 | private static final String MONTH = "mm"; | ||
| 14 | private static final String DAY = "dd"; | ||
| 15 | private static final String HOUR = "hh"; | ||
| 16 | private static final String MINUTE = "ii"; | ||
| 17 | private static final String SECOND = "ss"; | ||
| 18 | private static final String RAND = "rand"; | ||
| 19 | |||
| 20 | private static Date currentDate = null; | ||
| 21 | |||
| 22 | public static String parse ( String input ) { | ||
| 23 | |||
| 24 | Pattern pattern = Pattern.compile( "\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE ); | ||
| 25 | Matcher matcher = pattern.matcher(input); | ||
| 26 | |||
| 27 | PathFormat.currentDate = new Date(); | ||
| 28 | |||
| 29 | StringBuffer sb = new StringBuffer(); | ||
| 30 | |||
| 31 | while ( matcher.find() ) { | ||
| 32 | |||
| 33 | matcher.appendReplacement(sb, PathFormat.getString( matcher.group( 1 ) ) ); | ||
| 34 | |||
| 35 | } | ||
| 36 | |||
| 37 | matcher.appendTail(sb); | ||
| 38 | |||
| 39 | return sb.toString(); | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * 格式化路径, 把windows路径替换成标准路径 | ||
| 44 | * @param input 待格式化的路径 | ||
| 45 | * @return 格式化后的路径 | ||
| 46 | */ | ||
| 47 | public static String format ( String input ) { | ||
| 48 | |||
| 49 | return input.replace( "\\", "/" ); | ||
| 50 | |||
| 51 | } | ||
| 52 | |||
| 53 | public static String parse ( String input, String filename ) { | ||
| 54 | |||
| 55 | Pattern pattern = Pattern.compile( "\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE ); | ||
| 56 | Matcher matcher = pattern.matcher(input); | ||
| 57 | String matchStr = null; | ||
| 58 | |||
| 59 | PathFormat.currentDate = new Date(); | ||
| 60 | |||
| 61 | StringBuffer sb = new StringBuffer(); | ||
| 62 | |||
| 63 | while ( matcher.find() ) { | ||
| 64 | |||
| 65 | matchStr = matcher.group( 1 ); | ||
| 66 | if ( matchStr.indexOf( "filename" ) != -1 ) { | ||
| 67 | filename = filename.replace( "$", "\\$" ).replaceAll( "[\\/:*?\"<>|]", "" ); | ||
| 68 | matcher.appendReplacement(sb, filename ); | ||
| 69 | } else { | ||
| 70 | matcher.appendReplacement(sb, PathFormat.getString( matchStr ) ); | ||
| 71 | } | ||
| 72 | |||
| 73 | } | ||
| 74 | |||
| 75 | matcher.appendTail(sb); | ||
| 76 | |||
| 77 | return sb.toString(); | ||
| 78 | } | ||
| 79 | |||
| 80 | private static String getString ( String pattern ) { | ||
| 81 | |||
| 82 | pattern = pattern.toLowerCase(); | ||
| 83 | |||
| 84 | // time 处理 | ||
| 85 | if ( pattern.indexOf( PathFormat.TIME ) != -1 ) { | ||
| 86 | return PathFormat.getTimestamp(); | ||
| 87 | } else if ( pattern.indexOf( PathFormat.FULL_YEAR ) != -1 ) { | ||
| 88 | return PathFormat.getFullYear(); | ||
| 89 | } else if ( pattern.indexOf( PathFormat.YEAR ) != -1 ) { | ||
| 90 | return PathFormat.getYear(); | ||
| 91 | } else if ( pattern.indexOf( PathFormat.MONTH ) != -1 ) { | ||
| 92 | return PathFormat.getMonth(); | ||
| 93 | } else if ( pattern.indexOf( PathFormat.DAY ) != -1 ) { | ||
| 94 | return PathFormat.getDay(); | ||
| 95 | } else if ( pattern.indexOf( PathFormat.HOUR ) != -1 ) { | ||
| 96 | return PathFormat.getHour(); | ||
| 97 | } else if ( pattern.indexOf( PathFormat.MINUTE ) != -1 ) { | ||
| 98 | return PathFormat.getMinute(); | ||
| 99 | } else if ( pattern.indexOf( PathFormat.SECOND ) != -1 ) { | ||
| 100 | return PathFormat.getSecond(); | ||
| 101 | } else if ( pattern.indexOf( PathFormat.RAND ) != -1 ) { | ||
| 102 | return PathFormat.getRandom( pattern ); | ||
| 103 | } | ||
| 104 | |||
| 105 | return pattern; | ||
| 106 | |||
| 107 | } | ||
| 108 | |||
| 109 | private static String getTimestamp () { | ||
| 110 | return System.currentTimeMillis() + ""; | ||
| 111 | } | ||
| 112 | |||
| 113 | private static String getFullYear () { | ||
| 114 | return new SimpleDateFormat( "yyyy" ).format( PathFormat.currentDate ); | ||
| 115 | } | ||
| 116 | |||
| 117 | private static String getYear () { | ||
| 118 | return new SimpleDateFormat( "yy" ).format( PathFormat.currentDate ); | ||
| 119 | } | ||
| 120 | |||
| 121 | private static String getMonth () { | ||
| 122 | return new SimpleDateFormat( "MM" ).format( PathFormat.currentDate ); | ||
| 123 | } | ||
| 124 | |||
| 125 | private static String getDay () { | ||
| 126 | return new SimpleDateFormat( "dd" ).format( PathFormat.currentDate ); | ||
| 127 | } | ||
| 128 | |||
| 129 | private static String getHour () { | ||
| 130 | return new SimpleDateFormat( "HH" ).format( PathFormat.currentDate ); | ||
| 131 | } | ||
| 132 | |||
| 133 | private static String getMinute () { | ||
| 134 | return new SimpleDateFormat( "mm" ).format( PathFormat.currentDate ); | ||
| 135 | } | ||
| 136 | |||
| 137 | private static String getSecond () { | ||
| 138 | return new SimpleDateFormat( "ss" ).format( PathFormat.currentDate ); | ||
| 139 | } | ||
| 140 | |||
| 141 | private static String getRandom ( String pattern ) { | ||
| 142 | |||
| 143 | int length = 0; | ||
| 144 | pattern = pattern.split( ":" )[ 1 ].trim(); | ||
| 145 | |||
| 146 | length = Integer.parseInt( pattern ); | ||
| 147 | |||
| 148 | return ( Math.random() + "" ).replace( ".", "" ).substring( 0, length ); | ||
| 149 | |||
| 150 | } | ||
| 151 | |||
| 152 | public static void main(String[] args) { | ||
| 153 | // TODO Auto-generated method stub | ||
| 154 | |||
| 155 | } | ||
| 156 | |||
| 157 | } |
| 1 | package com.baidu.ueditor.define; | ||
| 2 | |||
| 3 | import java.util.HashMap; | ||
| 4 | import java.util.Map; | ||
| 5 | |||
| 6 | public final class AppInfo { | ||
| 7 | |||
| 8 | public static final int SUCCESS = 0; | ||
| 9 | public static final int MAX_SIZE = 1; | ||
| 10 | public static final int PERMISSION_DENIED = 2; | ||
| 11 | public static final int FAILED_CREATE_FILE = 3; | ||
| 12 | public static final int IO_ERROR = 4; | ||
| 13 | public static final int NOT_MULTIPART_CONTENT = 5; | ||
| 14 | public static final int PARSE_REQUEST_ERROR = 6; | ||
| 15 | public static final int NOTFOUND_UPLOAD_DATA = 7; | ||
| 16 | public static final int NOT_ALLOW_FILE_TYPE = 8; | ||
| 17 | |||
| 18 | public static final int INVALID_ACTION = 101; | ||
| 19 | public static final int CONFIG_ERROR = 102; | ||
| 20 | |||
| 21 | public static final int PREVENT_HOST = 201; | ||
| 22 | public static final int CONNECTION_ERROR = 202; | ||
| 23 | public static final int REMOTE_FAIL = 203; | ||
| 24 | |||
| 25 | public static final int NOT_DIRECTORY = 301; | ||
| 26 | public static final int NOT_EXIST = 302; | ||
| 27 | |||
| 28 | public static final int ILLEGAL = 401; | ||
| 29 | |||
| 30 | public static Map<Integer, String> info = new HashMap<Integer, String>(){{ | ||
| 31 | |||
| 32 | put( AppInfo.SUCCESS, "SUCCESS" ); | ||
| 33 | |||
| 34 | // 无效的Action | ||
| 35 | put( AppInfo.INVALID_ACTION, "\u65E0\u6548\u7684Action" ); | ||
| 36 | // 配置文件初始化失败 | ||
| 37 | put( AppInfo.CONFIG_ERROR, "\u914D\u7F6E\u6587\u4EF6\u521D\u59CB\u5316\u5931\u8D25" ); | ||
| 38 | // 抓取远程图片失败 | ||
| 39 | put( AppInfo.REMOTE_FAIL, "\u6293\u53D6\u8FDC\u7A0B\u56FE\u7247\u5931\u8D25" ); | ||
| 40 | |||
| 41 | // 被阻止的远程主机 | ||
| 42 | put( AppInfo.PREVENT_HOST, "\u88AB\u963B\u6B62\u7684\u8FDC\u7A0B\u4E3B\u673A" ); | ||
| 43 | // 远程连接出错 | ||
| 44 | put( AppInfo.CONNECTION_ERROR, "\u8FDC\u7A0B\u8FDE\u63A5\u51FA\u9519" ); | ||
| 45 | |||
| 46 | // "文件大小超出限制" | ||
| 47 | put( AppInfo.MAX_SIZE, "\u6587\u4ef6\u5927\u5c0f\u8d85\u51fa\u9650\u5236" ); | ||
| 48 | // 权限不足, 多指写权限 | ||
| 49 | put( AppInfo.PERMISSION_DENIED, "\u6743\u9650\u4E0D\u8DB3" ); | ||
| 50 | // 创建文件失败 | ||
| 51 | put( AppInfo.FAILED_CREATE_FILE, "\u521B\u5EFA\u6587\u4EF6\u5931\u8D25" ); | ||
| 52 | // IO错误 | ||
| 53 | put( AppInfo.IO_ERROR, "IO\u9519\u8BEF" ); | ||
| 54 | // 上传表单不是multipart/form-data类型 | ||
| 55 | put( AppInfo.NOT_MULTIPART_CONTENT, "\u4E0A\u4F20\u8868\u5355\u4E0D\u662Fmultipart/form-data\u7C7B\u578B" ); | ||
| 56 | // 解析上传表单错误 | ||
| 57 | put( AppInfo.PARSE_REQUEST_ERROR, "\u89E3\u6790\u4E0A\u4F20\u8868\u5355\u9519\u8BEF" ); | ||
| 58 | // 未找到上传数据 | ||
| 59 | put( AppInfo.NOTFOUND_UPLOAD_DATA, "\u672A\u627E\u5230\u4E0A\u4F20\u6570\u636E" ); | ||
| 60 | // 不允许的文件类型 | ||
| 61 | put( AppInfo.NOT_ALLOW_FILE_TYPE, "\u4E0D\u5141\u8BB8\u7684\u6587\u4EF6\u7C7B\u578B" ); | ||
| 62 | |||
| 63 | // 指定路径不是目录 | ||
| 64 | put( AppInfo.NOT_DIRECTORY, "\u6307\u5B9A\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55" ); | ||
| 65 | // 指定路径并不存在 | ||
| 66 | put( AppInfo.NOT_EXIST, "\u6307\u5B9A\u8DEF\u5F84\u5E76\u4E0D\u5B58\u5728" ); | ||
| 67 | |||
| 68 | // callback参数名不合法 | ||
| 69 | put( AppInfo.ILLEGAL, "Callback\u53C2\u6570\u540D\u4E0D\u5408\u6CD5" ); | ||
| 70 | |||
| 71 | }}; | ||
| 72 | |||
| 73 | public static String getStateInfo ( int key ) { | ||
| 74 | return AppInfo.info.get( key ); | ||
| 75 | } | ||
| 76 | |||
| 77 | } |
| 1 | package com.baidu.ueditor.define; | ||
| 2 | |||
| 3 | import java.util.HashMap; | ||
| 4 | import java.util.Iterator; | ||
| 5 | import java.util.Map; | ||
| 6 | |||
| 7 | import com.baidu.ueditor.Encoder; | ||
| 8 | |||
| 9 | public class BaseState implements State { | ||
| 10 | |||
| 11 | private boolean state = false; | ||
| 12 | private String info = null; | ||
| 13 | |||
| 14 | private Map<String, String> infoMap = new HashMap<String, String>(); | ||
| 15 | |||
| 16 | public BaseState () { | ||
| 17 | this.state = true; | ||
| 18 | } | ||
| 19 | |||
| 20 | public BaseState ( boolean state ) { | ||
| 21 | this.setState( state ); | ||
| 22 | } | ||
| 23 | |||
| 24 | public BaseState ( boolean state, String info ) { | ||
| 25 | this.setState( state ); | ||
| 26 | this.info = info; | ||
| 27 | } | ||
| 28 | |||
| 29 | public BaseState ( boolean state, int infoCode ) { | ||
| 30 | this.setState( state ); | ||
| 31 | this.info = AppInfo.getStateInfo( infoCode ); | ||
| 32 | } | ||
| 33 | |||
| 34 | public boolean isSuccess () { | ||
| 35 | return this.state; | ||
| 36 | } | ||
| 37 | |||
| 38 | public void setState ( boolean state ) { | ||
| 39 | this.state = state; | ||
| 40 | } | ||
| 41 | |||
| 42 | public void setInfo ( String info ) { | ||
| 43 | this.info = info; | ||
| 44 | } | ||
| 45 | |||
| 46 | public void setInfo ( int infoCode ) { | ||
| 47 | this.info = AppInfo.getStateInfo( infoCode ); | ||
| 48 | } | ||
| 49 | |||
| 50 | @Override | ||
| 51 | public String toJSONString() { | ||
| 52 | return this.toString(); | ||
| 53 | } | ||
| 54 | |||
| 55 | public String toString () { | ||
| 56 | |||
| 57 | String key = null; | ||
| 58 | String stateVal = this.isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this.info; | ||
| 59 | |||
| 60 | StringBuilder builder = new StringBuilder(); | ||
| 61 | |||
| 62 | builder.append( "{\"state\": \"" + stateVal + "\"" ); | ||
| 63 | |||
| 64 | Iterator<String> iterator = this.infoMap.keySet().iterator(); | ||
| 65 | |||
| 66 | while ( iterator.hasNext() ) { | ||
| 67 | |||
| 68 | key = iterator.next(); | ||
| 69 | |||
| 70 | builder.append( ",\"" + key + "\": \"" + this.infoMap.get(key) + "\"" ); | ||
| 71 | |||
| 72 | } | ||
| 73 | |||
| 74 | builder.append( "}" ); | ||
| 75 | |||
| 76 | return Encoder.toUnicode( builder.toString() ); | ||
| 77 | |||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | public void putInfo(String name, String val) { | ||
| 82 | this.infoMap.put(name, val); | ||
| 83 | } | ||
| 84 | |||
| 85 | @Override | ||
| 86 | public void putInfo(String name, long val) { | ||
| 87 | this.putInfo(name, val+""); | ||
| 88 | } | ||
| 89 | |||
| 90 | } |
| 1 | package com.baidu.ueditor.define; | ||
| 2 | |||
| 3 | import java.util.ArrayList; | ||
| 4 | import java.util.HashMap; | ||
| 5 | import java.util.Iterator; | ||
| 6 | import java.util.List; | ||
| 7 | import java.util.Map; | ||
| 8 | |||
| 9 | import com.baidu.ueditor.Encoder; | ||
| 10 | |||
| 11 | /** | ||
| 12 | * 多状态集合状态 | ||
| 13 | * 其包含了多个状态的集合, 其本身自己也是一个状态 | ||
| 14 | * @author hancong03@baidu.com | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | public class MultiState implements State { | ||
| 18 | |||
| 19 | private boolean state = false; | ||
| 20 | private String info = null; | ||
| 21 | private Map<String, Long> intMap = new HashMap<String, Long>(); | ||
| 22 | private Map<String, String> infoMap = new HashMap<String, String>(); | ||
| 23 | private List<String> stateList = new ArrayList<String>(); | ||
| 24 | |||
| 25 | public MultiState ( boolean state ) { | ||
| 26 | this.state = state; | ||
| 27 | } | ||
| 28 | |||
| 29 | public MultiState ( boolean state, String info ) { | ||
| 30 | this.state = state; | ||
| 31 | this.info = info; | ||
| 32 | } | ||
| 33 | |||
| 34 | public MultiState ( boolean state, int infoKey ) { | ||
| 35 | this.state = state; | ||
| 36 | this.info = AppInfo.getStateInfo( infoKey ); | ||
| 37 | } | ||
| 38 | |||
| 39 | @Override | ||
| 40 | public boolean isSuccess() { | ||
| 41 | return this.state; | ||
| 42 | } | ||
| 43 | |||
| 44 | public void addState ( State state ) { | ||
| 45 | stateList.add( state.toJSONString() ); | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * 该方法调用无效果 | ||
| 50 | */ | ||
| 51 | @Override | ||
| 52 | public void putInfo(String name, String val) { | ||
| 53 | this.infoMap.put(name, val); | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | public String toJSONString() { | ||
| 58 | |||
| 59 | String stateVal = this.isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this.info; | ||
| 60 | |||
| 61 | StringBuilder builder = new StringBuilder(); | ||
| 62 | |||
| 63 | builder.append( "{\"state\": \"" + stateVal + "\"" ); | ||
| 64 | |||
| 65 | // 数字转换 | ||
| 66 | Iterator<String> iterator = this.intMap.keySet().iterator(); | ||
| 67 | |||
| 68 | while ( iterator.hasNext() ) { | ||
| 69 | |||
| 70 | stateVal = iterator.next(); | ||
| 71 | |||
| 72 | builder.append( ",\""+ stateVal +"\": " + this.intMap.get( stateVal ) ); | ||
| 73 | |||
| 74 | } | ||
| 75 | |||
| 76 | iterator = this.infoMap.keySet().iterator(); | ||
| 77 | |||
| 78 | while ( iterator.hasNext() ) { | ||
| 79 | |||
| 80 | stateVal = iterator.next(); | ||
| 81 | |||
| 82 | builder.append( ",\""+ stateVal +"\": \"" + this.infoMap.get( stateVal ) + "\"" ); | ||
| 83 | |||
| 84 | } | ||
| 85 | |||
| 86 | builder.append( ", list: [" ); | ||
| 87 | |||
| 88 | |||
| 89 | iterator = this.stateList.iterator(); | ||
| 90 | |||
| 91 | while ( iterator.hasNext() ) { | ||
| 92 | |||
| 93 | builder.append( iterator.next() + "," ); | ||
| 94 | |||
| 95 | } | ||
| 96 | |||
| 97 | if ( this.stateList.size() > 0 ) { | ||
| 98 | builder.deleteCharAt( builder.length() - 1 ); | ||
| 99 | } | ||
| 100 | |||
| 101 | builder.append( " ]}" ); | ||
| 102 | |||
| 103 | return Encoder.toUnicode( builder.toString() ); | ||
| 104 | |||
| 105 | } | ||
| 106 | |||
| 107 | @Override | ||
| 108 | public void putInfo(String name, long val) { | ||
| 109 | this.intMap.put( name, val ); | ||
| 110 | } | ||
| 111 | |||
| 112 | } |
| 1 | package com.baidu.ueditor.hunter; | ||
| 2 | |||
| 3 | import java.io.File; | ||
| 4 | import java.util.Arrays; | ||
| 5 | import java.util.Collection; | ||
| 6 | import java.util.Map; | ||
| 7 | |||
| 8 | import org.apache.commons.io.FileUtils; | ||
| 9 | |||
| 10 | import com.baidu.ueditor.PathFormat; | ||
| 11 | import com.baidu.ueditor.define.AppInfo; | ||
| 12 | import com.baidu.ueditor.define.BaseState; | ||
| 13 | import com.baidu.ueditor.define.MultiState; | ||
| 14 | import com.baidu.ueditor.define.State; | ||
| 15 | |||
| 16 | public class FileManager { | ||
| 17 | |||
| 18 | private String dir = null; | ||
| 19 | private String rootPath = null; | ||
| 20 | private String[] allowFiles = null; | ||
| 21 | private int count = 0; | ||
| 22 | |||
| 23 | public FileManager ( Map<String, Object> conf ) { | ||
| 24 | |||
| 25 | this.rootPath = (String)conf.get( "rootPath" ); | ||
| 26 | this.dir = this.rootPath + (String)conf.get( "dir" ); | ||
| 27 | this.allowFiles = this.getAllowFiles( conf.get("allowFiles") ); | ||
| 28 | this.count = (Integer)conf.get( "count" ); | ||
| 29 | |||
| 30 | } | ||
| 31 | |||
| 32 | public State listFile ( int index ) { | ||
| 33 | |||
| 34 | File dir = new File( this.dir ); | ||
| 35 | State state = null; | ||
| 36 | |||
| 37 | if ( !dir.exists() ) { | ||
| 38 | return new BaseState( false, AppInfo.NOT_EXIST ); | ||
| 39 | } | ||
| 40 | |||
| 41 | if ( !dir.isDirectory() ) { | ||
| 42 | return new BaseState( false, AppInfo.NOT_DIRECTORY ); | ||
| 43 | } | ||
| 44 | |||
| 45 | Collection<File> list = FileUtils.listFiles( dir, this.allowFiles, true ); | ||
| 46 | |||
| 47 | if ( index < 0 || index > list.size() ) { | ||
| 48 | state = new MultiState( true ); | ||
| 49 | } else { | ||
| 50 | Object[] fileList = Arrays.copyOfRange( list.toArray(), index, index + this.count ); | ||
| 51 | state = this.getState( fileList ); | ||
| 52 | } | ||
| 53 | |||
| 54 | state.putInfo( "start", index ); | ||
| 55 | state.putInfo( "total", list.size() ); | ||
| 56 | |||
| 57 | return state; | ||
| 58 | |||
| 59 | } | ||
| 60 | |||
| 61 | private State getState ( Object[] files ) { | ||
| 62 | |||
| 63 | MultiState state = new MultiState( true ); | ||
| 64 | BaseState fileState = null; | ||
| 65 | |||
| 66 | File file = null; | ||
| 67 | |||
| 68 | for ( Object obj : files ) { | ||
| 69 | if ( obj == null ) { | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | file = (File)obj; | ||
| 73 | fileState = new BaseState( true ); | ||
| 74 | fileState.putInfo( "url", PathFormat.format( this.getPath( file ) ) ); | ||
| 75 | state.addState( fileState ); | ||
| 76 | } | ||
| 77 | |||
| 78 | return state; | ||
| 79 | |||
| 80 | } | ||
| 81 | |||
| 82 | private String getPath ( File file ) { | ||
| 83 | |||
| 84 | String path = file.getAbsolutePath(); | ||
| 85 | |||
| 86 | return path.replace( this.rootPath, "/" ); | ||
| 87 | |||
| 88 | } | ||
| 89 | |||
| 90 | private String[] getAllowFiles ( Object fileExt ) { | ||
| 91 | |||
| 92 | String[] exts = null; | ||
| 93 | String ext = null; | ||
| 94 | |||
| 95 | if ( fileExt == null ) { | ||
| 96 | return new String[ 0 ]; | ||
| 97 | } | ||
| 98 | |||
| 99 | exts = (String[])fileExt; | ||
| 100 | |||
| 101 | for ( int i = 0, len = exts.length; i < len; i++ ) { | ||
| 102 | |||
| 103 | ext = exts[ i ]; | ||
| 104 | exts[ i ] = ext.replace( ".", "" ); | ||
| 105 | |||
| 106 | } | ||
| 107 | |||
| 108 | return exts; | ||
| 109 | |||
| 110 | } | ||
| 111 | |||
| 112 | } |
| 1 | package com.baidu.ueditor.hunter; | ||
| 2 | |||
| 3 | import java.net.HttpURLConnection; | ||
| 4 | import java.net.URL; | ||
| 5 | import java.util.Arrays; | ||
| 6 | import java.util.List; | ||
| 7 | import java.util.Map; | ||
| 8 | |||
| 9 | import com.baidu.ueditor.PathFormat; | ||
| 10 | import com.baidu.ueditor.define.AppInfo; | ||
| 11 | import com.baidu.ueditor.define.BaseState; | ||
| 12 | import com.baidu.ueditor.define.MIMEType; | ||
| 13 | import com.baidu.ueditor.define.MultiState; | ||
| 14 | import com.baidu.ueditor.define.State; | ||
| 15 | import com.baidu.ueditor.upload.StorageManager; | ||
| 16 | |||
| 17 | /** | ||
| 18 | * 图片抓取器 | ||
| 19 | * @author hancong03@baidu.com | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | public class ImageHunter { | ||
| 23 | |||
| 24 | private String filename = null; | ||
| 25 | private String savePath = null; | ||
| 26 | private String rootPath = null; | ||
| 27 | private List<String> allowTypes = null; | ||
| 28 | private long maxSize = -1; | ||
| 29 | |||
| 30 | private List<String> filters = null; | ||
| 31 | |||
| 32 | public ImageHunter ( Map<String, Object> conf ) { | ||
| 33 | |||
| 34 | this.filename = (String)conf.get( "filename" ); | ||
| 35 | this.savePath = (String)conf.get( "savePath" ); | ||
| 36 | this.rootPath = (String)conf.get( "rootPath" ); | ||
| 37 | this.maxSize = (Long)conf.get( "maxSize" ); | ||
| 38 | this.allowTypes = Arrays.asList( (String[])conf.get( "allowFiles" ) ); | ||
| 39 | this.filters = Arrays.asList( (String[])conf.get( "filter" ) ); | ||
| 40 | |||
| 41 | } | ||
| 42 | |||
| 43 | public State capture ( String[] list ) { | ||
| 44 | |||
| 45 | MultiState state = new MultiState( true ); | ||
| 46 | |||
| 47 | for ( String source : list ) { | ||
| 48 | state.addState( captureRemoteData( source ) ); | ||
| 49 | } | ||
| 50 | |||
| 51 | return state; | ||
| 52 | |||
| 53 | } | ||
| 54 | |||
| 55 | public State captureRemoteData ( String urlStr ) { | ||
| 56 | |||
| 57 | HttpURLConnection connection = null; | ||
| 58 | URL url = null; | ||
| 59 | String suffix = null; | ||
| 60 | |||
| 61 | try { | ||
| 62 | url = new URL( urlStr ); | ||
| 63 | |||
| 64 | if ( !validHost( url.getHost() ) ) { | ||
| 65 | return new BaseState( false, AppInfo.PREVENT_HOST ); | ||
| 66 | } | ||
| 67 | |||
| 68 | connection = (HttpURLConnection) url.openConnection(); | ||
| 69 | |||
| 70 | connection.setInstanceFollowRedirects( true ); | ||
| 71 | connection.setUseCaches( true ); | ||
| 72 | |||
| 73 | if ( !validContentState( connection.getResponseCode() ) ) { | ||
| 74 | return new BaseState( false, AppInfo.CONNECTION_ERROR ); | ||
| 75 | } | ||
| 76 | |||
| 77 | suffix = MIMEType.getSuffix( connection.getContentType() ); | ||
| 78 | |||
| 79 | if ( !validFileType( suffix ) ) { | ||
| 80 | return new BaseState( false, AppInfo.NOT_ALLOW_FILE_TYPE ); | ||
| 81 | } | ||
| 82 | |||
| 83 | if ( !validFileSize( connection.getContentLength() ) ) { | ||
| 84 | return new BaseState( false, AppInfo.MAX_SIZE ); | ||
| 85 | } | ||
| 86 | |||
| 87 | String savePath = this.getPath( this.savePath, this.filename, suffix ); | ||
| 88 | String physicalPath = this.rootPath + savePath; | ||
| 89 | |||
| 90 | State state = StorageManager.saveFileByInputStream( connection.getInputStream(), physicalPath ); | ||
| 91 | |||
| 92 | if ( state.isSuccess() ) { | ||
| 93 | state.putInfo( "url", PathFormat.format( savePath ) ); | ||
| 94 | state.putInfo( "source", urlStr ); | ||
| 95 | } | ||
| 96 | |||
| 97 | return state; | ||
| 98 | |||
| 99 | } catch ( Exception e ) { | ||
| 100 | return new BaseState( false, AppInfo.REMOTE_FAIL ); | ||
| 101 | } | ||
| 102 | |||
| 103 | } | ||
| 104 | |||
| 105 | private String getPath ( String savePath, String filename, String suffix ) { | ||
| 106 | |||
| 107 | return PathFormat.parse( savePath + suffix, filename ); | ||
| 108 | |||
| 109 | } | ||
| 110 | |||
| 111 | private boolean validHost ( String hostname ) { | ||
| 112 | |||
| 113 | return !filters.contains( hostname ); | ||
| 114 | |||
| 115 | } | ||
| 116 | |||
| 117 | private boolean validContentState ( int code ) { | ||
| 118 | |||
| 119 | return HttpURLConnection.HTTP_OK == code; | ||
| 120 | |||
| 121 | } | ||
| 122 | |||
| 123 | private boolean validFileType ( String type ) { | ||
| 124 | |||
| 125 | return this.allowTypes.contains( type ); | ||
| 126 | |||
| 127 | } | ||
| 128 | |||
| 129 | private boolean validFileSize ( int size ) { | ||
| 130 | return size < this.maxSize; | ||
| 131 | } | ||
| 132 | |||
| 133 | } |
| 1 | package com.baidu.ueditor.upload; | ||
| 2 | |||
| 3 | import com.baidu.ueditor.PathFormat; | ||
| 4 | import com.baidu.ueditor.define.AppInfo; | ||
| 5 | import com.baidu.ueditor.define.BaseState; | ||
| 6 | import com.baidu.ueditor.define.FileType; | ||
| 7 | import com.baidu.ueditor.define.State; | ||
| 8 | |||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | import org.apache.commons.codec.binary.Base64; | ||
| 12 | |||
| 13 | public final class Base64Uploader { | ||
| 14 | |||
| 15 | public static State save(String content, Map<String, Object> conf) { | ||
| 16 | |||
| 17 | byte[] data = decode(content); | ||
| 18 | |||
| 19 | long maxSize = ((Long) conf.get("maxSize")).longValue(); | ||
| 20 | |||
| 21 | if (!validSize(data, maxSize)) { | ||
| 22 | return new BaseState(false, AppInfo.MAX_SIZE); | ||
| 23 | } | ||
| 24 | |||
| 25 | String suffix = FileType.getSuffix("JPG"); | ||
| 26 | |||
| 27 | String savePath = PathFormat.parse((String) conf.get("savePath"), | ||
| 28 | (String) conf.get("filename")); | ||
| 29 | |||
| 30 | savePath = savePath + suffix; | ||
| 31 | String physicalPath = (String) conf.get("rootPath") + savePath; | ||
| 32 | |||
| 33 | State storageState = StorageManager.saveBinaryFile(data, physicalPath); | ||
| 34 | |||
| 35 | if (storageState.isSuccess()) { | ||
| 36 | storageState.putInfo("url", PathFormat.format(savePath)); | ||
| 37 | storageState.putInfo("type", suffix); | ||
| 38 | storageState.putInfo("original", ""); | ||
| 39 | } | ||
| 40 | |||
| 41 | return storageState; | ||
| 42 | } | ||
| 43 | |||
| 44 | private static byte[] decode(String content) { | ||
| 45 | return Base64.decodeBase64(content); | ||
| 46 | } | ||
| 47 | |||
| 48 | private static boolean validSize(byte[] data, long length) { | ||
| 49 | return data.length <= length; | ||
| 50 | } | ||
| 51 | |||
| 52 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | package com.baidu.ueditor.upload; | ||
| 2 | |||
| 3 | import com.baidu.ueditor.PathFormat; | ||
| 4 | import com.baidu.ueditor.define.AppInfo; | ||
| 5 | import com.baidu.ueditor.define.BaseState; | ||
| 6 | import com.baidu.ueditor.define.FileType; | ||
| 7 | import com.baidu.ueditor.define.State; | ||
| 8 | |||
| 9 | import java.io.IOException; | ||
| 10 | import java.io.InputStream; | ||
| 11 | import java.util.Arrays; | ||
| 12 | import java.util.List; | ||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | import javax.servlet.http.HttpServletRequest; | ||
| 16 | |||
| 17 | import org.apache.commons.fileupload.FileItemIterator; | ||
| 18 | import org.apache.commons.fileupload.FileItemStream; | ||
| 19 | import org.apache.commons.fileupload.FileUploadException; | ||
| 20 | import org.apache.commons.fileupload.disk.DiskFileItemFactory; | ||
| 21 | import org.apache.commons.fileupload.servlet.ServletFileUpload; | ||
| 22 | |||
| 23 | public class BinaryUploader { | ||
| 24 | |||
| 25 | public static final State save(HttpServletRequest request, | ||
| 26 | Map<String, Object> conf) { | ||
| 27 | FileItemStream fileStream = null; | ||
| 28 | boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null; | ||
| 29 | |||
| 30 | if (!ServletFileUpload.isMultipartContent(request)) { | ||
| 31 | return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT); | ||
| 32 | } | ||
| 33 | |||
| 34 | ServletFileUpload upload = new ServletFileUpload( | ||
| 35 | new DiskFileItemFactory()); | ||
| 36 | |||
| 37 | if ( isAjaxUpload ) { | ||
| 38 | upload.setHeaderEncoding( "UTF-8" ); | ||
| 39 | } | ||
| 40 | |||
| 41 | try { | ||
| 42 | FileItemIterator iterator = upload.getItemIterator(request); | ||
| 43 | |||
| 44 | while (iterator.hasNext()) { | ||
| 45 | fileStream = iterator.next(); | ||
| 46 | |||
| 47 | if (!fileStream.isFormField()) | ||
| 48 | break; | ||
| 49 | fileStream = null; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (fileStream == null) { | ||
| 53 | return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA); | ||
| 54 | } | ||
| 55 | |||
| 56 | String savePath = (String) conf.get("savePath"); | ||
| 57 | String originFileName = fileStream.getName(); | ||
| 58 | String suffix = FileType.getSuffixByFilename(originFileName); | ||
| 59 | |||
| 60 | originFileName = originFileName.substring(0, | ||
| 61 | originFileName.length() - suffix.length()); | ||
| 62 | savePath = savePath + suffix; | ||
| 63 | |||
| 64 | long maxSize = ((Long) conf.get("maxSize")).longValue(); | ||
| 65 | |||
| 66 | if (!validType(suffix, (String[]) conf.get("allowFiles"))) { | ||
| 67 | return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE); | ||
| 68 | } | ||
| 69 | |||
| 70 | savePath = PathFormat.parse(savePath, originFileName); | ||
| 71 | |||
| 72 | String physicalPath = (String) conf.get("rootPath") + savePath; | ||
| 73 | |||
| 74 | InputStream is = fileStream.openStream(); | ||
| 75 | State storageState = StorageManager.saveFileByInputStream(is, | ||
| 76 | physicalPath, maxSize); | ||
| 77 | is.close(); | ||
| 78 | |||
| 79 | if (storageState.isSuccess()) { | ||
| 80 | storageState.putInfo("url", PathFormat.format(savePath)); | ||
| 81 | storageState.putInfo("type", suffix); | ||
| 82 | storageState.putInfo("original", originFileName + suffix); | ||
| 83 | } | ||
| 84 | |||
| 85 | return storageState; | ||
| 86 | } catch (FileUploadException e) { | ||
| 87 | return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR); | ||
| 88 | } catch (IOException e) { | ||
| 89 | } | ||
| 90 | return new BaseState(false, AppInfo.IO_ERROR); | ||
| 91 | } | ||
| 92 | |||
| 93 | private static boolean validType(String type, String[] allowTypes) { | ||
| 94 | List<String> list = Arrays.asList(allowTypes); | ||
| 95 | |||
| 96 | return list.contains(type); | ||
| 97 | } | ||
| 98 | } |
| 1 | package com.baidu.ueditor.upload; | ||
| 2 | |||
| 3 | import com.baidu.ueditor.define.AppInfo; | ||
| 4 | import com.baidu.ueditor.define.BaseState; | ||
| 5 | import com.baidu.ueditor.define.State; | ||
| 6 | |||
| 7 | import java.io.BufferedInputStream; | ||
| 8 | import java.io.BufferedOutputStream; | ||
| 9 | import java.io.File; | ||
| 10 | import java.io.FileOutputStream; | ||
| 11 | import java.io.IOException; | ||
| 12 | import java.io.InputStream; | ||
| 13 | |||
| 14 | import org.apache.commons.io.FileUtils; | ||
| 15 | |||
| 16 | public class StorageManager { | ||
| 17 | public static final int BUFFER_SIZE = 8192; | ||
| 18 | |||
| 19 | public StorageManager() { | ||
| 20 | } | ||
| 21 | |||
| 22 | public static State saveBinaryFile(byte[] data, String path) { | ||
| 23 | File file = new File(path); | ||
| 24 | |||
| 25 | State state = valid(file); | ||
| 26 | |||
| 27 | if (!state.isSuccess()) { | ||
| 28 | return state; | ||
| 29 | } | ||
| 30 | |||
| 31 | try { | ||
| 32 | BufferedOutputStream bos = new BufferedOutputStream( | ||
| 33 | new FileOutputStream(file)); | ||
| 34 | bos.write(data); | ||
| 35 | bos.flush(); | ||
| 36 | bos.close(); | ||
| 37 | } catch (IOException ioe) { | ||
| 38 | return new BaseState(false, AppInfo.IO_ERROR); | ||
| 39 | } | ||
| 40 | |||
| 41 | state = new BaseState(true, file.getAbsolutePath()); | ||
| 42 | state.putInfo( "size", data.length ); | ||
| 43 | state.putInfo( "title", file.getName() ); | ||
| 44 | return state; | ||
| 45 | } | ||
| 46 | |||
| 47 | public static State saveFileByInputStream(InputStream is, String path, | ||
| 48 | long maxSize) { | ||
| 49 | State state = null; | ||
| 50 | |||
| 51 | File tmpFile = getTmpFile(); | ||
| 52 | |||
| 53 | byte[] dataBuf = new byte[ 2048 ]; | ||
| 54 | BufferedInputStream bis = new BufferedInputStream(is, StorageManager.BUFFER_SIZE); | ||
| 55 | |||
| 56 | try { | ||
| 57 | BufferedOutputStream bos = new BufferedOutputStream( | ||
| 58 | new FileOutputStream(tmpFile), StorageManager.BUFFER_SIZE); | ||
| 59 | |||
| 60 | int count = 0; | ||
| 61 | while ((count = bis.read(dataBuf)) != -1) { | ||
| 62 | bos.write(dataBuf, 0, count); | ||
| 63 | } | ||
| 64 | bos.flush(); | ||
| 65 | bos.close(); | ||
| 66 | |||
| 67 | if (tmpFile.length() > maxSize) { | ||
| 68 | tmpFile.delete(); | ||
| 69 | return new BaseState(false, AppInfo.MAX_SIZE); | ||
| 70 | } | ||
| 71 | |||
| 72 | state = saveTmpFile(tmpFile, path); | ||
| 73 | |||
| 74 | if (!state.isSuccess()) { | ||
| 75 | tmpFile.delete(); | ||
| 76 | } | ||
| 77 | |||
| 78 | return state; | ||
| 79 | |||
| 80 | } catch (IOException e) { | ||
| 81 | } | ||
| 82 | return new BaseState(false, AppInfo.IO_ERROR); | ||
| 83 | } | ||
| 84 | |||
| 85 | public static State saveFileByInputStream(InputStream is, String path) { | ||
| 86 | State state = null; | ||
| 87 | |||
| 88 | File tmpFile = getTmpFile(); | ||
| 89 | |||
| 90 | byte[] dataBuf = new byte[ 2048 ]; | ||
| 91 | BufferedInputStream bis = new BufferedInputStream(is, StorageManager.BUFFER_SIZE); | ||
| 92 | |||
| 93 | try { | ||
| 94 | BufferedOutputStream bos = new BufferedOutputStream( | ||
| 95 | new FileOutputStream(tmpFile), StorageManager.BUFFER_SIZE); | ||
| 96 | |||
| 97 | int count = 0; | ||
| 98 | while ((count = bis.read(dataBuf)) != -1) { | ||
| 99 | bos.write(dataBuf, 0, count); | ||
| 100 | } | ||
| 101 | bos.flush(); | ||
| 102 | bos.close(); | ||
| 103 | |||
| 104 | state = saveTmpFile(tmpFile, path); | ||
| 105 | |||
| 106 | if (!state.isSuccess()) { | ||
| 107 | tmpFile.delete(); | ||
| 108 | } | ||
| 109 | |||
| 110 | return state; | ||
| 111 | } catch (IOException e) { | ||
| 112 | } | ||
| 113 | return new BaseState(false, AppInfo.IO_ERROR); | ||
| 114 | } | ||
| 115 | |||
| 116 | private static File getTmpFile() { | ||
| 117 | File tmpDir = FileUtils.getTempDirectory(); | ||
| 118 | String tmpFileName = (Math.random() * 10000 + "").replace(".", ""); | ||
| 119 | return new File(tmpDir, tmpFileName); | ||
| 120 | } | ||
| 121 | |||
| 122 | private static State saveTmpFile(File tmpFile, String path) { | ||
| 123 | State state = null; | ||
| 124 | File targetFile = new File(path); | ||
| 125 | |||
| 126 | if (targetFile.canWrite()) { | ||
| 127 | return new BaseState(false, AppInfo.PERMISSION_DENIED); | ||
| 128 | } | ||
| 129 | try { | ||
| 130 | FileUtils.moveFile(tmpFile, targetFile); | ||
| 131 | } catch (IOException e) { | ||
| 132 | return new BaseState(false, AppInfo.IO_ERROR); | ||
| 133 | } | ||
| 134 | |||
| 135 | state = new BaseState(true); | ||
| 136 | state.putInfo( "size", targetFile.length() ); | ||
| 137 | state.putInfo( "title", targetFile.getName() ); | ||
| 138 | |||
| 139 | return state; | ||
| 140 | } | ||
| 141 | |||
| 142 | private static State valid(File file) { | ||
| 143 | File parentPath = file.getParentFile(); | ||
| 144 | |||
| 145 | if ((!parentPath.exists()) && (!parentPath.mkdirs())) { | ||
| 146 | return new BaseState(false, AppInfo.FAILED_CREATE_FILE); | ||
| 147 | } | ||
| 148 | |||
| 149 | if (!parentPath.canWrite()) { | ||
| 150 | return new BaseState(false, AppInfo.PERMISSION_DENIED); | ||
| 151 | } | ||
| 152 | |||
| 153 | return new BaseState(true); | ||
| 154 | } | ||
| 155 | } |
3.29 KB
3.76 KB
4.81 KB
1.19 KB
3.66 KB
3.01 KB
952 Bytes
1007 Bytes
3.85 KB
4.22 KB
6.82 KB
6.45 KB
| 1 | using Newtonsoft.Json; | ||
| 2 | using Newtonsoft.Json.Linq; | ||
| 3 | using System; | ||
| 4 | using System.Collections.Generic; | ||
| 5 | using System.Dynamic; | ||
| 6 | using System.IO; | ||
| 7 | using System.Linq; | ||
| 8 | using System.Web; | ||
| 9 | |||
| 10 | /// <summary> | ||
| 11 | /// Config 的摘要说明 | ||
| 12 | /// </summary> | ||
| 13 | public static class Config | ||
| 14 | { | ||
| 15 | private static bool noCache = true; | ||
| 16 | private static JObject BuildItems() | ||
| 17 | { | ||
| 18 | var json = File.ReadAllText(HttpContext.Current.Server.MapPath("config.json")); | ||
| 19 | return JObject.Parse(json); | ||
| 20 | } | ||
| 21 | |||
| 22 | public static JObject Items | ||
| 23 | { | ||
| 24 | get | ||
| 25 | { | ||
| 26 | if (noCache || _Items == null) | ||
| 27 | { | ||
| 28 | _Items = BuildItems(); | ||
| 29 | } | ||
| 30 | return _Items; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | private static JObject _Items; | ||
| 34 | |||
| 35 | |||
| 36 | public static T GetValue<T>(string key) | ||
| 37 | { | ||
| 38 | return Items[key].Value<T>(); | ||
| 39 | } | ||
| 40 | |||
| 41 | public static String[] GetStringList(string key) | ||
| 42 | { | ||
| 43 | return Items[key].Select(x => x.Value<String>()).ToArray(); | ||
| 44 | } | ||
| 45 | |||
| 46 | public static String GetString(string key) | ||
| 47 | { | ||
| 48 | return GetValue<String>(key); | ||
| 49 | } | ||
| 50 | |||
| 51 | public static int GetInt(string key) | ||
| 52 | { | ||
| 53 | return GetValue<int>(key); | ||
| 54 | } | ||
| 55 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using System.Net; | ||
| 6 | using System.Web; | ||
| 7 | |||
| 8 | /// <summary> | ||
| 9 | /// Crawler 的摘要说明 | ||
| 10 | /// </summary> | ||
| 11 | public class CrawlerHandler : Handler | ||
| 12 | { | ||
| 13 | private string[] Sources; | ||
| 14 | private Crawler[] Crawlers; | ||
| 15 | public CrawlerHandler(HttpContext context) : base(context) { } | ||
| 16 | |||
| 17 | public override void Process() | ||
| 18 | { | ||
| 19 | Sources = Request.Form.GetValues("source[]"); | ||
| 20 | if (Sources == null || Sources.Length == 0) | ||
| 21 | { | ||
| 22 | WriteJson(new | ||
| 23 | { | ||
| 24 | state = "参数错误:没有指定抓取源" | ||
| 25 | }); | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | Crawlers = Sources.Select(x => new Crawler(x, Server).Fetch()).ToArray(); | ||
| 29 | WriteJson(new | ||
| 30 | { | ||
| 31 | state = "SUCCESS", | ||
| 32 | list = Crawlers.Select(x => new | ||
| 33 | { | ||
| 34 | state = x.State, | ||
| 35 | source = x.SourceUrl, | ||
| 36 | url = x.ServerUrl | ||
| 37 | }) | ||
| 38 | }); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | public class Crawler | ||
| 43 | { | ||
| 44 | public string SourceUrl { get; set; } | ||
| 45 | public string ServerUrl { get; set; } | ||
| 46 | public string State { get; set; } | ||
| 47 | |||
| 48 | private HttpServerUtility Server { get; set; } | ||
| 49 | |||
| 50 | |||
| 51 | public Crawler(string sourceUrl, HttpServerUtility server) | ||
| 52 | { | ||
| 53 | this.SourceUrl = sourceUrl; | ||
| 54 | this.Server = server; | ||
| 55 | } | ||
| 56 | |||
| 57 | public Crawler Fetch() | ||
| 58 | { | ||
| 59 | var request = HttpWebRequest.Create(this.SourceUrl) as HttpWebRequest; | ||
| 60 | using (var response = request.GetResponse() as HttpWebResponse) | ||
| 61 | { | ||
| 62 | if (response.StatusCode != HttpStatusCode.OK) | ||
| 63 | { | ||
| 64 | State = "Url returns " + response.StatusCode + ", " + response.StatusDescription; | ||
| 65 | return this; | ||
| 66 | } | ||
| 67 | if (response.ContentType.IndexOf("image") == -1) | ||
| 68 | { | ||
| 69 | State = "Url is not an image"; | ||
| 70 | return this; | ||
| 71 | } | ||
| 72 | ServerUrl = PathFormatter.Format(Path.GetFileName(this.SourceUrl), Config.GetString("catcherPathFormat")); | ||
| 73 | var savePath = Server.MapPath(ServerUrl); | ||
| 74 | if (!Directory.Exists(Path.GetDirectoryName(savePath))) | ||
| 75 | { | ||
| 76 | Directory.CreateDirectory(Path.GetDirectoryName(savePath)); | ||
| 77 | } | ||
| 78 | try | ||
| 79 | { | ||
| 80 | var stream = response.GetResponseStream(); | ||
| 81 | var reader = new BinaryReader(stream); | ||
| 82 | byte[] bytes; | ||
| 83 | using (var ms = new MemoryStream()) | ||
| 84 | { | ||
| 85 | byte[] buffer = new byte[4096]; | ||
| 86 | int count; | ||
| 87 | while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) | ||
| 88 | { | ||
| 89 | ms.Write(buffer, 0, count); | ||
| 90 | } | ||
| 91 | bytes = ms.ToArray(); | ||
| 92 | } | ||
| 93 | File.WriteAllBytes(savePath, bytes); | ||
| 94 | State = "SUCCESS"; | ||
| 95 | } | ||
| 96 | catch (Exception e) | ||
| 97 | { | ||
| 98 | State = "抓取错误:" + e.Message; | ||
| 99 | } | ||
| 100 | return this; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.Linq; | ||
| 4 | using System.Web; | ||
| 5 | using Newtonsoft.Json; | ||
| 6 | |||
| 7 | |||
| 8 | /// <summary> | ||
| 9 | /// Handler 的摘要说明 | ||
| 10 | /// </summary> | ||
| 11 | public abstract class Handler | ||
| 12 | { | ||
| 13 | public Handler(HttpContext context) | ||
| 14 | { | ||
| 15 | this.Request = context.Request; | ||
| 16 | this.Response = context.Response; | ||
| 17 | this.Context = context; | ||
| 18 | this.Server = context.Server; | ||
| 19 | } | ||
| 20 | |||
| 21 | public abstract void Process(); | ||
| 22 | |||
| 23 | protected void WriteJson(object response) | ||
| 24 | { | ||
| 25 | string jsonpCallback = Request["callback"], | ||
| 26 | json = JsonConvert.SerializeObject(response); | ||
| 27 | if (String.IsNullOrWhiteSpace(jsonpCallback)) | ||
| 28 | { | ||
| 29 | Response.AddHeader("Content-Type", "text/plain"); | ||
| 30 | Response.Write(json); | ||
| 31 | } | ||
| 32 | else | ||
| 33 | { | ||
| 34 | Response.AddHeader("Content-Type", "application/javascript"); | ||
| 35 | Response.Write(String.Format("{0}({1});", jsonpCallback, json)); | ||
| 36 | } | ||
| 37 | Response.End(); | ||
| 38 | } | ||
| 39 | |||
| 40 | public HttpRequest Request { get; private set; } | ||
| 41 | public HttpResponse Response { get; private set; } | ||
| 42 | public HttpContext Context { get; private set; } | ||
| 43 | public HttpServerUtility Server { get; private set; } | ||
| 44 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using System.Web; | ||
| 6 | |||
| 7 | /// <summary> | ||
| 8 | /// FileManager 的摘要说明 | ||
| 9 | /// </summary> | ||
| 10 | public class ListFileManager : Handler | ||
| 11 | { | ||
| 12 | enum ResultState | ||
| 13 | { | ||
| 14 | Success, | ||
| 15 | InvalidParam, | ||
| 16 | AuthorizError, | ||
| 17 | IOError, | ||
| 18 | PathNotFound | ||
| 19 | } | ||
| 20 | |||
| 21 | private int Start; | ||
| 22 | private int Size; | ||
| 23 | private int Total; | ||
| 24 | private ResultState State; | ||
| 25 | private String PathToList; | ||
| 26 | private String[] FileList; | ||
| 27 | private String[] SearchExtensions; | ||
| 28 | |||
| 29 | public ListFileManager(HttpContext context, string pathToList, string[] searchExtensions) | ||
| 30 | : base(context) | ||
| 31 | { | ||
| 32 | this.SearchExtensions = searchExtensions.Select(x => x.ToLower()).ToArray(); | ||
| 33 | this.PathToList = pathToList; | ||
| 34 | } | ||
| 35 | |||
| 36 | public override void Process() | ||
| 37 | { | ||
| 38 | try | ||
| 39 | { | ||
| 40 | Start = String.IsNullOrEmpty(Request["start"]) ? 0 : Convert.ToInt32(Request["start"]); | ||
| 41 | Size = String.IsNullOrEmpty(Request["size"]) ? Config.GetInt("imageManagerListSize") : Convert.ToInt32(Request["size"]); | ||
| 42 | } | ||
| 43 | catch (FormatException) | ||
| 44 | { | ||
| 45 | State = ResultState.InvalidParam; | ||
| 46 | WriteResult(); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | var buildingList = new List<String>(); | ||
| 50 | try | ||
| 51 | { | ||
| 52 | var localPath = Server.MapPath(PathToList); | ||
| 53 | buildingList.AddRange(Directory.GetFiles(localPath, "*", SearchOption.AllDirectories) | ||
| 54 | .Where(x => SearchExtensions.Contains(Path.GetExtension(x).ToLower())) | ||
| 55 | .Select(x => PathToList + x.Substring(localPath.Length).Replace("\\", "/"))); | ||
| 56 | Total = buildingList.Count; | ||
| 57 | FileList = buildingList.OrderBy(x => x).Skip(Start).Take(Size).ToArray(); | ||
| 58 | } | ||
| 59 | catch (UnauthorizedAccessException) | ||
| 60 | { | ||
| 61 | State = ResultState.AuthorizError; | ||
| 62 | } | ||
| 63 | catch (DirectoryNotFoundException) | ||
| 64 | { | ||
| 65 | State = ResultState.PathNotFound; | ||
| 66 | } | ||
| 67 | catch (IOException) | ||
| 68 | { | ||
| 69 | State = ResultState.IOError; | ||
| 70 | } | ||
| 71 | finally | ||
| 72 | { | ||
| 73 | WriteResult(); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | private void WriteResult() | ||
| 78 | { | ||
| 79 | WriteJson(new | ||
| 80 | { | ||
| 81 | state = GetStateString(), | ||
| 82 | list = FileList == null ? null : FileList.Select(x => new { url = x }), | ||
| 83 | start = Start, | ||
| 84 | size = Size, | ||
| 85 | total = Total | ||
| 86 | }); | ||
| 87 | } | ||
| 88 | |||
| 89 | private string GetStateString() | ||
| 90 | { | ||
| 91 | switch (State) | ||
| 92 | { | ||
| 93 | case ResultState.Success: | ||
| 94 | return "SUCCESS"; | ||
| 95 | case ResultState.InvalidParam: | ||
| 96 | return "参数不正确"; | ||
| 97 | case ResultState.PathNotFound: | ||
| 98 | return "路径不存在"; | ||
| 99 | case ResultState.AuthorizError: | ||
| 100 | return "文件系统权限不足"; | ||
| 101 | case ResultState.IOError: | ||
| 102 | return "文件系统读取错误"; | ||
| 103 | } | ||
| 104 | return "未知错误"; | ||
| 105 | } | ||
| 106 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | | ||
| 2 | using System; | ||
| 3 | using System.Collections.Generic; | ||
| 4 | using System.IO; | ||
| 5 | using System.Linq; | ||
| 6 | using System.Text.RegularExpressions; | ||
| 7 | using System.Web; | ||
| 8 | |||
| 9 | /// <summary> | ||
| 10 | /// PathFormater 的摘要说明 | ||
| 11 | /// </summary> | ||
| 12 | public static class PathFormatter | ||
| 13 | { | ||
| 14 | public static string Format(string originFileName, string pathFormat) | ||
| 15 | { | ||
| 16 | if (String.IsNullOrWhiteSpace(pathFormat)) | ||
| 17 | { | ||
| 18 | pathFormat = "{filename}{rand:6}"; | ||
| 19 | } | ||
| 20 | |||
| 21 | var invalidPattern = new Regex(@"[\\\/\:\*\?\042\<\>\|]"); | ||
| 22 | originFileName = invalidPattern.Replace(originFileName, ""); | ||
| 23 | |||
| 24 | string extension = Path.GetExtension(originFileName); | ||
| 25 | string filename = Path.GetFileNameWithoutExtension(originFileName); | ||
| 26 | |||
| 27 | pathFormat = pathFormat.Replace("{filename}", filename); | ||
| 28 | pathFormat = new Regex(@"\{rand(\:?)(\d+)\}", RegexOptions.Compiled).Replace(pathFormat, new MatchEvaluator(delegate(Match match) | ||
| 29 | { | ||
| 30 | var digit = 6; | ||
| 31 | if (match.Groups.Count > 2) | ||
| 32 | { | ||
| 33 | digit = Convert.ToInt32(match.Groups[2].Value); | ||
| 34 | } | ||
| 35 | var rand = new Random(); | ||
| 36 | return rand.Next((int)Math.Pow(10, digit), (int)Math.Pow(10, digit + 1)).ToString(); | ||
| 37 | })); | ||
| 38 | |||
| 39 | pathFormat = pathFormat.Replace("{time}", DateTime.Now.Ticks.ToString()); | ||
| 40 | pathFormat = pathFormat.Replace("{yyyy}", DateTime.Now.Year.ToString()); | ||
| 41 | pathFormat = pathFormat.Replace("{yy}", (DateTime.Now.Year % 100).ToString("D2")); | ||
| 42 | pathFormat = pathFormat.Replace("{mm}", DateTime.Now.Month.ToString("D2")); | ||
| 43 | pathFormat = pathFormat.Replace("{dd}", DateTime.Now.Day.ToString("D2")); | ||
| 44 | pathFormat = pathFormat.Replace("{hh}", DateTime.Now.Hour.ToString("D2")); | ||
| 45 | pathFormat = pathFormat.Replace("{ii}", DateTime.Now.Minute.ToString("D2")); | ||
| 46 | pathFormat = pathFormat.Replace("{ss}", DateTime.Now.Second.ToString("D2")); | ||
| 47 | |||
| 48 | return pathFormat + extension; | ||
| 49 | } | ||
| 50 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using System.Text.RegularExpressions; | ||
| 6 | using System.Web; | ||
| 7 | |||
| 8 | /// <summary> | ||
| 9 | /// UploadHandler 的摘要说明 | ||
| 10 | /// </summary> | ||
| 11 | public class UploadHandler : Handler | ||
| 12 | { | ||
| 13 | |||
| 14 | public UploadConfig UploadConfig { get; private set; } | ||
| 15 | public UploadResult Result { get; private set; } | ||
| 16 | |||
| 17 | public UploadHandler(HttpContext context, UploadConfig config) | ||
| 18 | : base(context) | ||
| 19 | { | ||
| 20 | this.UploadConfig = config; | ||
| 21 | this.Result = new UploadResult() { State = UploadState.Unknown }; | ||
| 22 | } | ||
| 23 | |||
| 24 | public override void Process() | ||
| 25 | { | ||
| 26 | byte[] uploadFileBytes = null; | ||
| 27 | string uploadFileName = null; | ||
| 28 | |||
| 29 | if (UploadConfig.Base64) | ||
| 30 | { | ||
| 31 | uploadFileName = UploadConfig.Base64Filename; | ||
| 32 | uploadFileBytes = Convert.FromBase64String(Request[UploadConfig.UploadFieldName]); | ||
| 33 | } | ||
| 34 | else | ||
| 35 | { | ||
| 36 | var file = Request.Files[UploadConfig.UploadFieldName]; | ||
| 37 | uploadFileName = file.FileName; | ||
| 38 | |||
| 39 | if (!CheckFileType(uploadFileName)) | ||
| 40 | { | ||
| 41 | Result.State = UploadState.TypeNotAllow; | ||
| 42 | WriteResult(); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | if (!CheckFileSize(file.ContentLength)) | ||
| 46 | { | ||
| 47 | Result.State = UploadState.SizeLimitExceed; | ||
| 48 | WriteResult(); | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | |||
| 52 | uploadFileBytes = new byte[file.ContentLength]; | ||
| 53 | try | ||
| 54 | { | ||
| 55 | file.InputStream.Read(uploadFileBytes, 0, file.ContentLength); | ||
| 56 | } | ||
| 57 | catch (Exception) | ||
| 58 | { | ||
| 59 | Result.State = UploadState.NetworkError; | ||
| 60 | WriteResult(); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | Result.OriginFileName = uploadFileName; | ||
| 65 | |||
| 66 | var savePath = PathFormatter.Format(uploadFileName, UploadConfig.PathFormat); | ||
| 67 | var localPath = Server.MapPath(savePath); | ||
| 68 | try | ||
| 69 | { | ||
| 70 | if (!Directory.Exists(Path.GetDirectoryName(localPath))) | ||
| 71 | { | ||
| 72 | Directory.CreateDirectory(Path.GetDirectoryName(localPath)); | ||
| 73 | } | ||
| 74 | File.WriteAllBytes(localPath, uploadFileBytes); | ||
| 75 | Result.Url = savePath; | ||
| 76 | Result.State = UploadState.Success; | ||
| 77 | } | ||
| 78 | catch (Exception e) | ||
| 79 | { | ||
| 80 | Result.State = UploadState.FileAccessError; | ||
| 81 | Result.ErrorMessage = e.Message; | ||
| 82 | } | ||
| 83 | finally | ||
| 84 | { | ||
| 85 | WriteResult(); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | private void WriteResult() | ||
| 90 | { | ||
| 91 | this.WriteJson(new | ||
| 92 | { | ||
| 93 | state = GetStateMessage(Result.State), | ||
| 94 | url = Result.Url, | ||
| 95 | title = Result.OriginFileName, | ||
| 96 | original = Result.OriginFileName, | ||
| 97 | error = Result.ErrorMessage | ||
| 98 | }); | ||
| 99 | } | ||
| 100 | |||
| 101 | private string GetStateMessage(UploadState state) | ||
| 102 | { | ||
| 103 | switch (state) | ||
| 104 | { | ||
| 105 | case UploadState.Success: | ||
| 106 | return "SUCCESS"; | ||
| 107 | case UploadState.FileAccessError: | ||
| 108 | return "文件访问出错,请检查写入权限"; | ||
| 109 | case UploadState.SizeLimitExceed: | ||
| 110 | return "文件大小超出服务器限制"; | ||
| 111 | case UploadState.TypeNotAllow: | ||
| 112 | return "不允许的文件格式"; | ||
| 113 | case UploadState.NetworkError: | ||
| 114 | return "网络错误"; | ||
| 115 | } | ||
| 116 | return "未知错误"; | ||
| 117 | } | ||
| 118 | |||
| 119 | private bool CheckFileType(string filename) | ||
| 120 | { | ||
| 121 | var fileExtension = Path.GetExtension(filename).ToLower(); | ||
| 122 | return UploadConfig.AllowExtensions.Select(x => x.ToLower()).Contains(fileExtension); | ||
| 123 | } | ||
| 124 | |||
| 125 | private bool CheckFileSize(int size) | ||
| 126 | { | ||
| 127 | return size < UploadConfig.SizeLimit; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | public class UploadConfig | ||
| 132 | { | ||
| 133 | /// <summary> | ||
| 134 | /// 文件命名规则 | ||
| 135 | /// </summary> | ||
| 136 | public string PathFormat { get; set; } | ||
| 137 | |||
| 138 | /// <summary> | ||
| 139 | /// 上传表单域名称 | ||
| 140 | /// </summary> | ||
| 141 | public string UploadFieldName { get; set; } | ||
| 142 | |||
| 143 | /// <summary> | ||
| 144 | /// 上传大小限制 | ||
| 145 | /// </summary> | ||
| 146 | public int SizeLimit { get; set; } | ||
| 147 | |||
| 148 | /// <summary> | ||
| 149 | /// 上传允许的文件格式 | ||
| 150 | /// </summary> | ||
| 151 | public string[] AllowExtensions { get; set; } | ||
| 152 | |||
| 153 | /// <summary> | ||
| 154 | /// 文件是否以 Base64 的形式上传 | ||
| 155 | /// </summary> | ||
| 156 | public bool Base64 { get; set; } | ||
| 157 | |||
| 158 | /// <summary> | ||
| 159 | /// Base64 字符串所表示的文件名 | ||
| 160 | /// </summary> | ||
| 161 | public string Base64Filename { get; set; } | ||
| 162 | } | ||
| 163 | |||
| 164 | public class UploadResult | ||
| 165 | { | ||
| 166 | public UploadState State { get; set; } | ||
| 167 | public string Url { get; set; } | ||
| 168 | public string OriginFileName { get; set; } | ||
| 169 | |||
| 170 | public string ErrorMessage { get; set; } | ||
| 171 | } | ||
| 172 | |||
| 173 | public enum UploadState | ||
| 174 | { | ||
| 175 | Success = 0, | ||
| 176 | SizeLimitExceed = -1, | ||
| 177 | TypeNotAllow = -2, | ||
| 178 | FileAccessError = -3, | ||
| 179 | NetworkError = -4, | ||
| 180 | Unknown = 1, | ||
| 181 | } | ||
| 182 |
| 1 | UEditor ASP.NET 后台使用说明 | ||
| 2 | ===== | ||
| 3 | |||
| 4 | ## 背景 | ||
| 5 | |||
| 6 | UEditor 在 1.4 版本之后进行了一次[前后端统一配置](../_doc/3.1 后端请求规范.md)的整理,.Net 的后台也进行了一次重写,跟之前的版本差别较大,升级的用户注意阅读本文档。 | ||
| 7 | |||
| 8 | 本文档介绍 UEditor ASP.NET 后台的部署、配置、源码说明。 | ||
| 9 | |||
| 10 | |||
| 11 | ## 1. 部署说明 | ||
| 12 | |||
| 13 | ### 1.1. 安装并注册 .NET Framework 4.0 | ||
| 14 | |||
| 15 | 代码的运行时环境是 .NET Framework 4.0,首先要确认 IIS 已经安装了 .NET 4.0 的运行时框架。方法是打开「IIS 管理器」,选择根目录下的「应用程序池」,在右侧查看是否有一个应用程序池的版本是 v4.0,如果存在,则 IIS 已经安装了所需的运行时环境,此时读者可以跳过本节。 | ||
| 16 | |||
| 17 |  | ||
| 18 | |||
| 19 | 如果没有找到对应的应用程序池,需要手动安装。 | ||
| 20 | |||
| 21 | Windows 7 和 Windows Server 2008 R2 默认安装了 .Net Framework 4.0,如果是 Server 03 和老掉牙的 Windows XP,则需要手动安装 [.NET Framework 4.0](http://www.microsoft.com/zh-cn/download/details.aspx?id=17718)。 | ||
| 22 | |||
| 23 | 安装完 .NET Framework 4.0 后,还需要向 IIS 注册应用程序池,注册的方法是,使用**管理员权限**打开命令提示符(CMD),输入以下命令: | ||
| 24 | |||
| 25 | ```shell | ||
| 26 | C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -i | ||
| 27 | ``` | ||
| 28 | |||
| 29 | 安装完毕后,在 IIS 管理器刷新就能看到 4.0 的应用程序池。 | ||
| 30 | |||
| 31 | ### 1.2. 设置 .NET 应用程序 | ||
| 32 | |||
| 33 | 代码要求以应用程序的形式来运行(可以方便加入库依赖和组织代码)。需要把 `net` 目录转换为应用程序。 | ||
| 34 | |||
| 35 | 1. 在 IIS 中,展开到 `ueditor/net` 目录,在目录上右击,点击「转换为应用程序」。 | ||
| 36 | |||
| 37 |  | ||
| 38 | |||
| 39 | 2. 弹出的对话框中,点击「选择...」来指定使用的应用程序池。选择版本为 4.0 的应用程序池,然后点确定。 | ||
| 40 | |||
| 41 |  | ||
| 42 | |||
| 43 | 3. 设置连接凭据。点击「链接为...」按钮,在弹出的对话框中指定一个对目录具有读写权限的用户(如 administrator),然后点确定。 | ||
| 44 | |||
| 45 |  | ||
| 46 | |||
| 47 | 设置完毕后,可以点击「测试设置...」来测试权限是否正常。 | ||
| 48 | |||
| 49 |  | ||
| 50 | |||
| 51 | ### 1.3. 运行测试 | ||
| 52 | |||
| 53 | 在浏览器中运行 `net/controller.ashx`,如果返回 "`{"state":"action 参数为空或者 action 不被支持。"}`",则表示应用程序运行成功。 | ||
| 54 | |||
| 55 | 如果你确认上述步骤已经执行,但是依然有问题,请给我们[提 Issue](https://github.com/fex-team/ueditor/issues/new?labels=NET%E5%90%8E%E5%8F%B0),我们会尽快答复解决。 | ||
| 56 | |||
| 57 | ## 2. 配置说明 | ||
| 58 | |||
| 59 | 前后端配置统一之后,配置文件由后台读取,返回给前端。但是部分配置是给后台使用的。 | ||
| 60 | |||
| 61 | ### 2.1. 上传配置说明 | ||
| 62 | |||
| 63 | 关于上传的部分,后台需要关心以下模板的配置项。 | ||
| 64 | |||
| 65 | ```json | ||
| 66 | { | ||
| 67 | "{tpl}FieldName": "upfile", | ||
| 68 | "{tpl}PathFormat": "upload/{tpl}/{yyyy}{mm}{dd}/{time}{rand:6}", | ||
| 69 | "{tpl}UrlPrefix": "/ueditor/net/", | ||
| 70 | "{tpl}AllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], | ||
| 71 | "{tpl}MaxSize": 2048000 | ||
| 72 | } | ||
| 73 | ``` | ||
| 74 | |||
| 75 | "{tpl}FieldName" 表示提交的表单的文件域名称。 | ||
| 76 | |||
| 77 | "{tpl}PathFormat" 表示上传文件保存的路径和名称。注意,这里的路径是相对应用程序的,如果需要修改的话,请自行修改源码。 | ||
| 78 | |||
| 79 | "{tpl}UrlPrefix" 表示上传文件访问的 URL 前缀。注意,这里应该给出应用程序的 URL 路径,否则上传的文件不能正确定位。 | ||
| 80 | |||
| 81 | > 举个例子,如果你的 UEditor 的位置在 `http://www.mydomain.com/myapp/ueditor`,对应的本地路径是 `C:\iis_pub\www\myapp\ueditor`,那么 .NET 应用程序的位置在 `http://www.mydomain.com/myapp/ueditor/net`,对应的本地路径是 `C:\iis_pub\www\myapp\ueditor\net`。图片上传配置项应该如下: | ||
| 82 | > | ||
| 83 | > { | ||
| 84 | > "imagePathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", | ||
| 85 | > "imageUrlPrefix": "/myapp/ueditor/net/", | ||
| 86 | > } | ||
| 87 | > | ||
| 88 | > 上传的文件会保存在 `C:\iis_pub\www\myapp\ueditor\net\upload\image\{日期}\{文件名}` | ||
| 89 | |||
| 90 | "{tpl}AllowFiles" 限制文件上传的类型,注意要有 "."。 | ||
| 91 | |||
| 92 | "{tpl}MaxSize" 限制文件上传的大小。注意这里的限制是代码上的判断,应用程序本身还有一个请求报文大小限制。该限制在 web.config 文件中修改,注意要有以下的节: | ||
| 93 | |||
| 94 | ```xml | ||
| 95 | <configuration> | ||
| 96 | <system.web> | ||
| 97 | <httpRuntime requestValidationMode="2.0" maxRequestLength="102400" /> | ||
| 98 | </system.web> | ||
| 99 | </configuration> | ||
| 100 | ``` | ||
| 101 | |||
| 102 | maxRequestLength 就是请求报文大小限制,该大小应该要比设置的所有上传大小都大,否则应用程序执行之前,请求会被被拒绝。 | ||
| 103 | |||
| 104 | ## 3. 源码说明 | ||
| 105 | |||
| 106 | 可以看到 net 目录内的源码结构是这样的: | ||
| 107 | |||
| 108 | ``` | ||
| 109 | net | ||
| 110 | App_Code | ||
| 111 | Config.cs | ||
| 112 | Handler.cs | ||
| 113 | PathFormatter.cs | ||
| 114 | *Handler.cs | ||
| 115 | Bin | ||
| 116 | Newtonsoft.Json.dll | ||
| 117 | config.json | ||
| 118 | controller.ashx | ||
| 119 | net.sln | ||
| 120 | README.md | ||
| 121 | Web.config | ||
| 122 | ``` | ||
| 123 | |||
| 124 | App_Code 上的文件是应用程序的源码。 | ||
| 125 | |||
| 126 | - Config.cs 负责读取配置文件 | ||
| 127 | - Handler.cs 是请求处理器的基类,提供了一些基本对象的访问以及输出控制。如果需要增加处理器,应该从该基类继承 | ||
| 128 | - PathFormatter.cs 解析 PathFormat,把信息填充为运行时信息。 | ||
| 129 | - *Handler.cs 是各种处理器,处理各种 UEditor 需要的请求。 | ||
| 130 | |||
| 131 | Bin 里面的是应用程序的依赖库,当前依赖 Newtonsoft 的 Json 库。Bin 目录和 App_Code 目录受应用程序保护,不用担心被用户访问到。 | ||
| 132 | |||
| 133 | config.json 是 UEditor 后端的配置文件,上一节已经介绍了比较重要的配置项。 | ||
| 134 | |||
| 135 | controller.ashx 是 UEditor 请求的入口,它把不同的 action 分发到不同的 Handler 来处理。 | ||
| 136 | |||
| 137 | net.sln 是项目的解决方案文件,安装 Visual Studio 2013 或以上的机器可以打开进行项目的改造。 | ||
| 138 | |||
| 139 | README.md 是本说明文件。 | ||
| 140 | |||
| 141 | Web.config 是应用程序的配置文件。 |
| 1 | /* 前后端通信相关的配置,注释只允许使用多行方式 */ | ||
| 2 | { | ||
| 3 | /* 上传图片配置项 */ | ||
| 4 | "imageActionName": "uploadimage", /* 执行上传图片的action名称 */ | ||
| 5 | "imageFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 6 | "imageMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 7 | "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */ | ||
| 8 | "imageCompressEnable": true, /* 是否压缩图片,默认是true */ | ||
| 9 | "imageCompressBorder": 1600, /* 图片压缩最长边限制 */ | ||
| 10 | "imageInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 11 | "imageUrlPrefix": "/ueditor/net/", /* 图片访问路径前缀 */ | ||
| 12 | "imagePathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 13 | /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */ | ||
| 14 | /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */ | ||
| 15 | /* {time} 会替换成时间戳 */ | ||
| 16 | /* {yyyy} 会替换成四位年份 */ | ||
| 17 | /* {yy} 会替换成两位年份 */ | ||
| 18 | /* {mm} 会替换成两位月份 */ | ||
| 19 | /* {dd} 会替换成两位日期 */ | ||
| 20 | /* {hh} 会替换成两位小时 */ | ||
| 21 | /* {ii} 会替换成两位分钟 */ | ||
| 22 | /* {ss} 会替换成两位秒 */ | ||
| 23 | /* 非法字符 \ : * ? " < > | */ | ||
| 24 | /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */ | ||
| 25 | |||
| 26 | /* 涂鸦图片上传配置项 */ | ||
| 27 | "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */ | ||
| 28 | "scrawlFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 29 | "scrawlPathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 30 | "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 31 | "scrawlUrlPrefix": "/ueditor/net/", /* 图片访问路径前缀 */ | ||
| 32 | "scrawlInsertAlign": "none", | ||
| 33 | |||
| 34 | /* 截图工具上传 */ | ||
| 35 | "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */ | ||
| 36 | "snapscreenPathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 37 | "snapscreenUrlPrefix": "/ueditor/net/", /* 图片访问路径前缀 */ | ||
| 38 | "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 39 | |||
| 40 | /* 抓取远程图片配置 */ | ||
| 41 | "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], | ||
| 42 | "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */ | ||
| 43 | "catcherFieldName": "source", /* 提交的图片列表表单名称 */ | ||
| 44 | "catcherPathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 45 | "catcherUrlPrefix": "/ueditor/net/", /* 图片访问路径前缀 */ | ||
| 46 | "catcherMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 47 | "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */ | ||
| 48 | |||
| 49 | /* 上传视频配置 */ | ||
| 50 | "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */ | ||
| 51 | "videoFieldName": "upfile", /* 提交的视频表单名称 */ | ||
| 52 | "videoPathFormat": "upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 53 | "videoUrlPrefix": "/ueditor/net/", /* 视频访问路径前缀 */ | ||
| 54 | "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */ | ||
| 55 | "videoAllowFiles": [ | ||
| 56 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 57 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */ | ||
| 58 | |||
| 59 | /* 上传文件配置 */ | ||
| 60 | "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */ | ||
| 61 | "fileFieldName": "upfile", /* 提交的文件表单名称 */ | ||
| 62 | "filePathFormat": "upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 63 | "fileUrlPrefix": "/ueditor/net/", /* 文件访问路径前缀 */ | ||
| 64 | "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */ | ||
| 65 | "fileAllowFiles": [ | ||
| 66 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 67 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 68 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 69 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 70 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 71 | ], /* 上传文件格式显示 */ | ||
| 72 | |||
| 73 | /* 列出指定目录下的图片 */ | ||
| 74 | "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */ | ||
| 75 | "imageManagerListPath": "upload/image", /* 指定要列出图片的目录 */ | ||
| 76 | "imageManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 77 | "imageManagerUrlPrefix": "/ueditor/net/", /* 图片访问路径前缀 */ | ||
| 78 | "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 79 | "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */ | ||
| 80 | |||
| 81 | /* 列出指定目录下的文件 */ | ||
| 82 | "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */ | ||
| 83 | "fileManagerListPath": "upload/file", /* 指定要列出文件的目录 */ | ||
| 84 | "fileManagerUrlPrefix": "/ueditor/net/", /* 文件访问路径前缀 */ | ||
| 85 | "fileManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 86 | "fileManagerAllowFiles": [ | ||
| 87 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 88 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 89 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 90 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 91 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 92 | ] /* 列出的文件类型 */ | ||
| 93 | |||
| 94 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <%@ WebHandler Language="C#" Class="UEditorHandler" %> | ||
| 2 | |||
| 3 | using System; | ||
| 4 | using System.Web; | ||
| 5 | using System.IO; | ||
| 6 | using System.Collections; | ||
| 7 | using Newtonsoft.Json; | ||
| 8 | |||
| 9 | public class UEditorHandler : IHttpHandler | ||
| 10 | { | ||
| 11 | public void ProcessRequest(HttpContext context) | ||
| 12 | { | ||
| 13 | Handler action = null; | ||
| 14 | switch (context.Request["action"]) | ||
| 15 | { | ||
| 16 | case "config": | ||
| 17 | action = new ConfigHandler(context); | ||
| 18 | break; | ||
| 19 | case "uploadimage": | ||
| 20 | action = new UploadHandler(context, new UploadConfig() | ||
| 21 | { | ||
| 22 | AllowExtensions = Config.GetStringList("imageAllowFiles"), | ||
| 23 | PathFormat = Config.GetString("imagePathFormat"), | ||
| 24 | SizeLimit = Config.GetInt("imageMaxSize"), | ||
| 25 | UploadFieldName = Config.GetString("imageFieldName") | ||
| 26 | }); | ||
| 27 | break; | ||
| 28 | case "uploadscrawl": | ||
| 29 | action = new UploadHandler(context, new UploadConfig() | ||
| 30 | { | ||
| 31 | AllowExtensions = new string[] { ".png" }, | ||
| 32 | PathFormat = Config.GetString("scrawlPathFormat"), | ||
| 33 | SizeLimit = Config.GetInt("scrawlMaxSize"), | ||
| 34 | UploadFieldName = Config.GetString("scrawlFieldName"), | ||
| 35 | Base64 = true, | ||
| 36 | Base64Filename = "scrawl.png" | ||
| 37 | }); | ||
| 38 | break; | ||
| 39 | case "uploadvideo": | ||
| 40 | action = new UploadHandler(context, new UploadConfig() | ||
| 41 | { | ||
| 42 | AllowExtensions = Config.GetStringList("videoAllowFiles"), | ||
| 43 | PathFormat = Config.GetString("videoPathFormat"), | ||
| 44 | SizeLimit = Config.GetInt("videoMaxSize"), | ||
| 45 | UploadFieldName = Config.GetString("videoFieldName") | ||
| 46 | }); | ||
| 47 | break; | ||
| 48 | case "uploadfile": | ||
| 49 | action = new UploadHandler(context, new UploadConfig() | ||
| 50 | { | ||
| 51 | AllowExtensions = Config.GetStringList("fileAllowFiles"), | ||
| 52 | PathFormat = Config.GetString("filePathFormat"), | ||
| 53 | SizeLimit = Config.GetInt("fileMaxSize"), | ||
| 54 | UploadFieldName = Config.GetString("fileFieldName") | ||
| 55 | }); | ||
| 56 | break; | ||
| 57 | case "listimage": | ||
| 58 | action = new ListFileManager(context, Config.GetString("imageManagerListPath"), Config.GetStringList("imageManagerAllowFiles")); | ||
| 59 | break; | ||
| 60 | case "listfile": | ||
| 61 | action = new ListFileManager(context, Config.GetString("fileManagerListPath"), Config.GetStringList("fileManagerAllowFiles")); | ||
| 62 | break; | ||
| 63 | case "catchimage": | ||
| 64 | action = new CrawlerHandler(context); | ||
| 65 | break; | ||
| 66 | default: | ||
| 67 | action = new NotSupportedHandler(context); | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | action.Process(); | ||
| 71 | } | ||
| 72 | |||
| 73 | public bool IsReusable | ||
| 74 | { | ||
| 75 | get | ||
| 76 | { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | | ||
| 2 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| 3 | # Visual Studio 2013 | ||
| 4 | VisualStudioVersion = 12.0.21005.1 | ||
| 5 | MinimumVisualStudioVersion = 10.0.40219.1 | ||
| 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "net", "http://localhost:7957", "{36F65A7F-64E7-4E05-BBC2-EAB6E4EDAF30}" | ||
| 7 | ProjectSection(WebsiteProperties) = preProject | ||
| 8 | UseIISExpress = "true" | ||
| 9 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" | ||
| 10 | Debug.AspNetCompiler.VirtualPath = "/localhost_7957" | ||
| 11 | Debug.AspNetCompiler.PhysicalPath = "..\..\..\..\prj\ueditor\net\" | ||
| 12 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_7957\" | ||
| 13 | Debug.AspNetCompiler.Updateable = "true" | ||
| 14 | Debug.AspNetCompiler.ForceOverwrite = "true" | ||
| 15 | Debug.AspNetCompiler.FixedNames = "false" | ||
| 16 | Debug.AspNetCompiler.Debug = "True" | ||
| 17 | Release.AspNetCompiler.VirtualPath = "/localhost_7957" | ||
| 18 | Release.AspNetCompiler.PhysicalPath = "..\..\..\..\prj\ueditor\net\" | ||
| 19 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_7957\" | ||
| 20 | Release.AspNetCompiler.Updateable = "true" | ||
| 21 | Release.AspNetCompiler.ForceOverwrite = "true" | ||
| 22 | Release.AspNetCompiler.FixedNames = "false" | ||
| 23 | Release.AspNetCompiler.Debug = "False" | ||
| 24 | SlnRelativePath = "..\..\..\..\prj\ueditor\net\" | ||
| 25 | EndProjectSection | ||
| 26 | EndProject | ||
| 27 | Global | ||
| 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| 29 | Debug|Any CPU = Debug|Any CPU | ||
| 30 | EndGlobalSection | ||
| 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| 32 | {36F65A7F-64E7-4E05-BBC2-EAB6E4EDAF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| 33 | {36F65A7F-64E7-4E05-BBC2-EAB6E4EDAF30}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| 34 | EndGlobalSection | ||
| 35 | GlobalSection(SolutionProperties) = preSolution | ||
| 36 | HideSolutionNode = FALSE | ||
| 37 | EndGlobalSection | ||
| 38 | EndGlobal |
| 1 | <?php | ||
| 2 | /** | ||
| 3 | * 抓取远程图片 | ||
| 4 | * User: Jinqn | ||
| 5 | * Date: 14-04-14 | ||
| 6 | * Time: 下午19:18 | ||
| 7 | */ | ||
| 8 | set_time_limit(0); | ||
| 9 | include("Uploader.class.php"); | ||
| 10 | |||
| 11 | /* 上传配置 */ | ||
| 12 | $config = array( | ||
| 13 | "pathFormat" => $CONFIG['catcherPathFormat'], | ||
| 14 | "maxSize" => $CONFIG['catcherMaxSize'], | ||
| 15 | "allowFiles" => $CONFIG['catcherAllowFiles'], | ||
| 16 | "oriName" => "remote.png" | ||
| 17 | ); | ||
| 18 | $fieldName = $CONFIG['catcherFieldName']; | ||
| 19 | |||
| 20 | /* 抓取远程图片 */ | ||
| 21 | $list = array(); | ||
| 22 | if (isset($_POST[$fieldName])) { | ||
| 23 | $source = $_POST[$fieldName]; | ||
| 24 | } else { | ||
| 25 | $source = $_GET[$fieldName]; | ||
| 26 | } | ||
| 27 | foreach ($source as $imgUrl) { | ||
| 28 | $item = new Uploader($imgUrl, $config, "remote"); | ||
| 29 | $info = $item->getFileInfo(); | ||
| 30 | array_push($list, array( | ||
| 31 | "state" => $info["state"], | ||
| 32 | "url" => $info["url"], | ||
| 33 | "size" => $info["size"], | ||
| 34 | "title" => htmlspecialchars($info["title"]), | ||
| 35 | "original" => htmlspecialchars($info["original"]), | ||
| 36 | "source" => htmlspecialchars($imgUrl) | ||
| 37 | )); | ||
| 38 | } | ||
| 39 | |||
| 40 | /* 返回抓取数据 */ | ||
| 41 | return json_encode(array( | ||
| 42 | 'state'=> count($list) ? 'SUCCESS':'ERROR', | ||
| 43 | 'list'=> $list | ||
| 44 | )); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <?php | ||
| 2 | /** | ||
| 3 | * 获取已上传的文件列表 | ||
| 4 | * User: Jinqn | ||
| 5 | * Date: 14-04-09 | ||
| 6 | * Time: 上午10:17 | ||
| 7 | */ | ||
| 8 | include "Uploader.class.php"; | ||
| 9 | |||
| 10 | /* 判断类型 */ | ||
| 11 | switch ($_GET['action']) { | ||
| 12 | /* 列出文件 */ | ||
| 13 | case 'listfile': | ||
| 14 | $allowFiles = $CONFIG['fileManagerAllowFiles']; | ||
| 15 | $listSize = $CONFIG['fileManagerListSize']; | ||
| 16 | $path = $CONFIG['fileManagerListPath']; | ||
| 17 | break; | ||
| 18 | /* 列出图片 */ | ||
| 19 | case 'listimage': | ||
| 20 | default: | ||
| 21 | $allowFiles = $CONFIG['imageManagerAllowFiles']; | ||
| 22 | $listSize = $CONFIG['imageManagerListSize']; | ||
| 23 | $path = $CONFIG['imageManagerListPath']; | ||
| 24 | } | ||
| 25 | $allowFiles = substr(str_replace(".", "|", join("", $allowFiles)), 1); | ||
| 26 | |||
| 27 | /* 获取参数 */ | ||
| 28 | $size = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : $listSize; | ||
| 29 | $start = isset($_GET['start']) ? htmlspecialchars($_GET['start']) : 0; | ||
| 30 | $end = $start + $size; | ||
| 31 | |||
| 32 | /* 获取文件列表 */ | ||
| 33 | $path = $_SERVER['DOCUMENT_ROOT'] . (substr($path, 0, 1) == "/" ? "":"/") . $path; | ||
| 34 | $files = getfiles($path, $allowFiles); | ||
| 35 | if (!count($files)) { | ||
| 36 | return json_encode(array( | ||
| 37 | "state" => "no match file", | ||
| 38 | "list" => array(), | ||
| 39 | "start" => $start, | ||
| 40 | "total" => count($files) | ||
| 41 | )); | ||
| 42 | } | ||
| 43 | |||
| 44 | /* 获取指定范围的列表 */ | ||
| 45 | $len = count($files); | ||
| 46 | for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){ | ||
| 47 | $list[] = $files[$i]; | ||
| 48 | } | ||
| 49 | //倒序 | ||
| 50 | //for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){ | ||
| 51 | // $list[] = $files[$i]; | ||
| 52 | //} | ||
| 53 | |||
| 54 | /* 返回数据 */ | ||
| 55 | $result = json_encode(array( | ||
| 56 | "state" => "SUCCESS", | ||
| 57 | "list" => $list, | ||
| 58 | "start" => $start, | ||
| 59 | "total" => count($files) | ||
| 60 | )); | ||
| 61 | |||
| 62 | return $result; | ||
| 63 | |||
| 64 | |||
| 65 | /** | ||
| 66 | * 遍历获取目录下的指定类型的文件 | ||
| 67 | * @param $path | ||
| 68 | * @param array $files | ||
| 69 | * @return array | ||
| 70 | */ | ||
| 71 | function getfiles($path, $allowFiles, &$files = array()) | ||
| 72 | { | ||
| 73 | if (!is_dir($path)) return null; | ||
| 74 | if(substr($path, strlen($path) - 1) != '/') $path .= '/'; | ||
| 75 | $handle = opendir($path); | ||
| 76 | while (false !== ($file = readdir($handle))) { | ||
| 77 | if ($file != '.' && $file != '..') { | ||
| 78 | $path2 = $path . $file; | ||
| 79 | if (is_dir($path2)) { | ||
| 80 | getfiles($path2, $allowFiles, $files); | ||
| 81 | } else { | ||
| 82 | if (preg_match("/\.(".$allowFiles.")$/i", $file)) { | ||
| 83 | $files[] = array( | ||
| 84 | 'url'=> substr($path2, strlen($_SERVER['DOCUMENT_ROOT'])), | ||
| 85 | 'mtime'=> filemtime($path2) | ||
| 86 | ); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | return $files; | ||
| 92 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <?php | ||
| 2 | /** | ||
| 3 | * 上传附件和上传视频 | ||
| 4 | * User: Jinqn | ||
| 5 | * Date: 14-04-09 | ||
| 6 | * Time: 上午10:17 | ||
| 7 | */ | ||
| 8 | include "Uploader.class.php"; | ||
| 9 | |||
| 10 | /* 上传配置 */ | ||
| 11 | $base64 = "upload"; | ||
| 12 | switch (htmlspecialchars($_GET['action'])) { | ||
| 13 | case 'uploadimage': | ||
| 14 | $config = array( | ||
| 15 | "pathFormat" => $CONFIG['imagePathFormat'], | ||
| 16 | "maxSize" => $CONFIG['imageMaxSize'], | ||
| 17 | "allowFiles" => $CONFIG['imageAllowFiles'] | ||
| 18 | ); | ||
| 19 | $fieldName = $CONFIG['imageFieldName']; | ||
| 20 | break; | ||
| 21 | case 'uploadscrawl': | ||
| 22 | $config = array( | ||
| 23 | "pathFormat" => $CONFIG['scrawlPathFormat'], | ||
| 24 | "maxSize" => $CONFIG['scrawlMaxSize'], | ||
| 25 | "allowFiles" => $CONFIG['scrawlAllowFiles'], | ||
| 26 | "oriName" => "scrawl.png" | ||
| 27 | ); | ||
| 28 | $fieldName = $CONFIG['scrawlFieldName']; | ||
| 29 | $base64 = "base64"; | ||
| 30 | break; | ||
| 31 | case 'uploadvideo': | ||
| 32 | $config = array( | ||
| 33 | "pathFormat" => $CONFIG['videoPathFormat'], | ||
| 34 | "maxSize" => $CONFIG['videoMaxSize'], | ||
| 35 | "allowFiles" => $CONFIG['videoAllowFiles'] | ||
| 36 | ); | ||
| 37 | $fieldName = $CONFIG['videoFieldName']; | ||
| 38 | break; | ||
| 39 | case 'uploadfile': | ||
| 40 | default: | ||
| 41 | $config = array( | ||
| 42 | "pathFormat" => $CONFIG['filePathFormat'], | ||
| 43 | "maxSize" => $CONFIG['fileMaxSize'], | ||
| 44 | "allowFiles" => $CONFIG['fileAllowFiles'] | ||
| 45 | ); | ||
| 46 | $fieldName = $CONFIG['fileFieldName']; | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | |||
| 50 | /* 生成上传实例对象并完成上传 */ | ||
| 51 | $up = new Uploader($fieldName, $config, $base64); | ||
| 52 | |||
| 53 | /** | ||
| 54 | * 得到上传文件所对应的各个参数,数组结构 | ||
| 55 | * array( | ||
| 56 | * "state" => "", //上传状态,上传成功时必须返回"SUCCESS" | ||
| 57 | * "url" => "", //返回的地址 | ||
| 58 | * "title" => "", //新文件名 | ||
| 59 | * "original" => "", //原始文件名 | ||
| 60 | * "type" => "" //文件类型 | ||
| 61 | * "size" => "", //文件大小 | ||
| 62 | * ) | ||
| 63 | */ | ||
| 64 | |||
| 65 | /* 返回数据 */ | ||
| 66 | return json_encode($up->getFileInfo()); |
| 1 | /* 前后端通信相关的配置,注释只允许使用多行方式 */ | ||
| 2 | { | ||
| 3 | /* 上传图片配置项 */ | ||
| 4 | "imageActionName": "uploadimage", /* 执行上传图片的action名称 */ | ||
| 5 | "imageFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 6 | "imageMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 7 | "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */ | ||
| 8 | "imageCompressEnable": true, /* 是否压缩图片,默认是true */ | ||
| 9 | "imageCompressBorder": 1600, /* 图片压缩最长边限制 */ | ||
| 10 | "imageInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 11 | "imageUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 12 | "imagePathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 13 | /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */ | ||
| 14 | /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */ | ||
| 15 | /* {time} 会替换成时间戳 */ | ||
| 16 | /* {yyyy} 会替换成四位年份 */ | ||
| 17 | /* {yy} 会替换成两位年份 */ | ||
| 18 | /* {mm} 会替换成两位月份 */ | ||
| 19 | /* {dd} 会替换成两位日期 */ | ||
| 20 | /* {hh} 会替换成两位小时 */ | ||
| 21 | /* {ii} 会替换成两位分钟 */ | ||
| 22 | /* {ss} 会替换成两位秒 */ | ||
| 23 | /* 非法字符 \ : * ? " < > | */ | ||
| 24 | /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */ | ||
| 25 | |||
| 26 | /* 涂鸦图片上传配置项 */ | ||
| 27 | "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */ | ||
| 28 | "scrawlFieldName": "upfile", /* 提交的图片表单名称 */ | ||
| 29 | "scrawlPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 30 | "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 31 | "scrawlUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 32 | "scrawlInsertAlign": "none", | ||
| 33 | |||
| 34 | /* 截图工具上传 */ | ||
| 35 | "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */ | ||
| 36 | "snapscreenPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 37 | "snapscreenUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 38 | "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 39 | |||
| 40 | /* 抓取远程图片配置 */ | ||
| 41 | "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], | ||
| 42 | "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */ | ||
| 43 | "catcherFieldName": "source", /* 提交的图片列表表单名称 */ | ||
| 44 | "catcherPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 45 | "catcherUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 46 | "catcherMaxSize": 2048000, /* 上传大小限制,单位B */ | ||
| 47 | "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */ | ||
| 48 | |||
| 49 | /* 上传视频配置 */ | ||
| 50 | "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */ | ||
| 51 | "videoFieldName": "upfile", /* 提交的视频表单名称 */ | ||
| 52 | "videoPathFormat": "/ueditor/php/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 53 | "videoUrlPrefix": "", /* 视频访问路径前缀 */ | ||
| 54 | "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */ | ||
| 55 | "videoAllowFiles": [ | ||
| 56 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 57 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */ | ||
| 58 | |||
| 59 | /* 上传文件配置 */ | ||
| 60 | "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */ | ||
| 61 | "fileFieldName": "upfile", /* 提交的文件表单名称 */ | ||
| 62 | "filePathFormat": "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ | ||
| 63 | "fileUrlPrefix": "", /* 文件访问路径前缀 */ | ||
| 64 | "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */ | ||
| 65 | "fileAllowFiles": [ | ||
| 66 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 67 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 68 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 69 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 70 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 71 | ], /* 上传文件格式显示 */ | ||
| 72 | |||
| 73 | /* 列出指定目录下的图片 */ | ||
| 74 | "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */ | ||
| 75 | "imageManagerListPath": "/ueditor/php/upload/image/", /* 指定要列出图片的目录 */ | ||
| 76 | "imageManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 77 | "imageManagerUrlPrefix": "", /* 图片访问路径前缀 */ | ||
| 78 | "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */ | ||
| 79 | "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */ | ||
| 80 | |||
| 81 | /* 列出指定目录下的文件 */ | ||
| 82 | "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */ | ||
| 83 | "fileManagerListPath": "/ueditor/php/upload/file/", /* 指定要列出文件的目录 */ | ||
| 84 | "fileManagerUrlPrefix": "", /* 文件访问路径前缀 */ | ||
| 85 | "fileManagerListSize": 20, /* 每次列出文件数量 */ | ||
| 86 | "fileManagerAllowFiles": [ | ||
| 87 | ".png", ".jpg", ".jpeg", ".gif", ".bmp", | ||
| 88 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", | ||
| 89 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", | ||
| 90 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", | ||
| 91 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" | ||
| 92 | ] /* 列出的文件类型 */ | ||
| 93 | |||
| 94 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <?php | ||
| 2 | //header('Access-Control-Allow-Origin: http://www.baidu.com'); //设置http://www.baidu.com允许跨域访问 | ||
| 3 | //header('Access-Control-Allow-Headers: X-Requested-With,X_Requested_With'); //设置允许的跨域header | ||
| 4 | date_default_timezone_set("Asia/chongqing"); | ||
| 5 | error_reporting(E_ERROR); | ||
| 6 | header("Content-Type: text/html; charset=utf-8"); | ||
| 7 | |||
| 8 | $CONFIG = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents("config.json")), true); | ||
| 9 | $action = $_GET['action']; | ||
| 10 | |||
| 11 | switch ($action) { | ||
| 12 | case 'config': | ||
| 13 | $result = json_encode($CONFIG); | ||
| 14 | break; | ||
| 15 | |||
| 16 | /* 上传图片 */ | ||
| 17 | case 'uploadimage': | ||
| 18 | /* 上传涂鸦 */ | ||
| 19 | case 'uploadscrawl': | ||
| 20 | /* 上传视频 */ | ||
| 21 | case 'uploadvideo': | ||
| 22 | /* 上传文件 */ | ||
| 23 | case 'uploadfile': | ||
| 24 | $result = include("action_upload.php"); | ||
| 25 | break; | ||
| 26 | |||
| 27 | /* 列出图片 */ | ||
| 28 | case 'listimage': | ||
| 29 | $result = include("action_list.php"); | ||
| 30 | break; | ||
| 31 | /* 列出文件 */ | ||
| 32 | case 'listfile': | ||
| 33 | $result = include("action_list.php"); | ||
| 34 | break; | ||
| 35 | |||
| 36 | /* 抓取远程文件 */ | ||
| 37 | case 'catchimage': | ||
| 38 | $result = include("action_crawler.php"); | ||
| 39 | break; | ||
| 40 | |||
| 41 | default: | ||
| 42 | $result = json_encode(array( | ||
| 43 | 'state'=> '请求地址出错' | ||
| 44 | )); | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* 输出结果 */ | ||
| 49 | if (isset($_GET["callback"])) { | ||
| 50 | if (preg_match("/^[\w_]+$/", $_GET["callback"])) { | ||
| 51 | echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')'; | ||
| 52 | } else { | ||
| 53 | echo json_encode(array( | ||
| 54 | 'state'=> 'callback参数不合法' | ||
| 55 | )); | ||
| 56 | } | ||
| 57 | } else { | ||
| 58 | echo $result; | ||
| 59 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | /*弹出对话框页面样式组件 | ||
| 2 | */ | ||
| 3 | |||
| 4 | /*reset | ||
| 5 | */ | ||
| 6 | html, body, div, span, applet, object, iframe, | ||
| 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, | ||
| 8 | a, abbr, acronym, address, big, cite, code, | ||
| 9 | del, dfn, em, font, img, ins, kbd, q, s, samp, | ||
| 10 | small, strike, strong, sub, sup, tt, var, | ||
| 11 | b, u, i, center, | ||
| 12 | dl, dt, dd, ol, ul, li, | ||
| 13 | fieldset, form, label, legend, | ||
| 14 | table, caption, tbody, tfoot, thead, tr, th, td { | ||
| 15 | margin: 0; | ||
| 16 | padding: 0; | ||
| 17 | outline: 0; | ||
| 18 | font-size: 100%; | ||
| 19 | } | ||
| 20 | |||
| 21 | body { | ||
| 22 | line-height: 1; | ||
| 23 | } | ||
| 24 | |||
| 25 | ol, ul { | ||
| 26 | list-style: none; | ||
| 27 | } | ||
| 28 | |||
| 29 | blockquote, q { | ||
| 30 | quotes: none; | ||
| 31 | } | ||
| 32 | |||
| 33 | ins { | ||
| 34 | text-decoration: none; | ||
| 35 | } | ||
| 36 | |||
| 37 | del { | ||
| 38 | text-decoration: line-through; | ||
| 39 | } | ||
| 40 | |||
| 41 | table { | ||
| 42 | border-collapse: collapse; | ||
| 43 | border-spacing: 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | /*module | ||
| 47 | */ | ||
| 48 | body { | ||
| 49 | background-color: #fff; | ||
| 50 | font: 12px/1.5 sans-serif, "宋体", "Arial Narrow", HELVETICA; | ||
| 51 | color: #646464; | ||
| 52 | } | ||
| 53 | |||
| 54 | /*tab*/ | ||
| 55 | .tabhead { | ||
| 56 | position: relative; | ||
| 57 | z-index: 10; | ||
| 58 | } | ||
| 59 | |||
| 60 | .tabhead span { | ||
| 61 | display: inline-block; | ||
| 62 | padding: 0 5px; | ||
| 63 | height: 30px; | ||
| 64 | border: 1px solid #ccc; | ||
| 65 | background: url("images/dialog-title-bg.png") repeat-x; | ||
| 66 | text-align: center; | ||
| 67 | line-height: 30px; | ||
| 68 | cursor: pointer; | ||
| 69 | *margin-right: 5px; | ||
| 70 | } | ||
| 71 | |||
| 72 | .tabhead span.focus { | ||
| 73 | height: 31px; | ||
| 74 | border-bottom: none; | ||
| 75 | background: #fff; | ||
| 76 | } | ||
| 77 | |||
| 78 | .tabbody { | ||
| 79 | position: relative; | ||
| 80 | top: -1px; | ||
| 81 | margin: 0 auto; | ||
| 82 | border: 1px solid #ccc; | ||
| 83 | } | ||
| 84 | |||
| 85 | /*button*/ | ||
| 86 | a.button { | ||
| 87 | display: block; | ||
| 88 | text-align: center; | ||
| 89 | line-height: 24px; | ||
| 90 | text-decoration: none; | ||
| 91 | height: 24px; | ||
| 92 | width: 95px; | ||
| 93 | border: 0; | ||
| 94 | color: #838383; | ||
| 95 | background: url(../../themes/default/images/icons-all.gif) no-repeat; | ||
| 96 | } | ||
| 97 | |||
| 98 | a.button:hover { | ||
| 99 | background-position: 0 -30px; | ||
| 100 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
1.15 KB
1.57 KB
1.61 KB
1.09 KB
1.2 KB
938 Bytes
4.18 KB
111 Bytes
3.66 KB
20.5 KB
19.2 KB
3.13 KB
734 Bytes
1.04 KB
54 Bytes
2.78 KB
43 Bytes
122 Bytes
111 Bytes
6.45 KB
1.57 KB
1019 Bytes
6.32 KB
| 1 | /*可以在这里添加你自己的css*/ |
| 1 | .syntaxhighlighter a,.syntaxhighlighter div,.syntaxhighlighter code,.syntaxhighlighter,.syntaxhighlighter td,.syntaxhighlighter tr,.syntaxhighlighter tbody,.syntaxhighlighter thead,.syntaxhighlighter caption,.syntaxhighlighter textarea{-moz-border-radius:0 0 0 0!important;-webkit-border-radius:0 0 0 0!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-weight:normal!important;font-style:normal!important;min-height:inherit!important;min-height:auto!important;font-size:13px!important}.syntaxhighlighter{width:100%!important;margin:.3em 0 .3em 0!important;position:relative!important;overflow:auto!important;background-color:#f5f5f5!important;border:1px solid #ccc!important;border-radius:4px!important;border-collapse:separate!important}.syntaxhighlighter.source{overflow:hidden!important}.syntaxhighlighter .bold{font-weight:bold!important}.syntaxhighlighter .italic{font-style:italic!important}.syntaxhighlighter .gutter div{white-space:pre!important;word-wrap:normal}.syntaxhighlighter caption{text-align:left!important;padding:.5em 0 .5em 1em!important}.syntaxhighlighter td.code{width:100%!important}.syntaxhighlighter td.code .container{position:relative!important}.syntaxhighlighter td.code .container textarea{box-sizing:border-box!important;position:absolute!important;left:0!important;top:0!important;width:100%!important;border:none!important;background:white!important;padding-left:1em!important;overflow:hidden!important;white-space:pre!important}.syntaxhighlighter td.gutter .line{text-align:right!important;padding:0 .5em 0 1em!important}.syntaxhighlighter td.code .line{padding:0 1em!important}.syntaxhighlighter.nogutter td.code .container textarea,.syntaxhighlighter.nogutter td.code .line{padding-left:0!important}.syntaxhighlighter.show{display:block!important}.syntaxhighlighter.collapsed table{display:none!important}.syntaxhighlighter.collapsed .toolbar{padding:.1em .8em 0 .8em!important;font-size:1em!important;position:static!important;width:auto!important}.syntaxhighlighter.collapsed .toolbar span{display:inline!important;margin-right:1em!important}.syntaxhighlighter.collapsed .toolbar span a{padding:0!important;display:none!important}.syntaxhighlighter.collapsed .toolbar span a.expandSource{display:inline!important}.syntaxhighlighter .toolbar{position:absolute!important;right:1px!important;top:1px!important;width:11px!important;height:11px!important;font-size:10px!important;z-index:10!important}.syntaxhighlighter .toolbar span.title{display:inline!important}.syntaxhighlighter .toolbar a{display:block!important;text-align:center!important;text-decoration:none!important;padding-top:1px!important}.syntaxhighlighter .toolbar a.expandSource{display:none!important}.syntaxhighlighter.ie{font-size:.9em!important;padding:1px 0 1px 0!important}.syntaxhighlighter.ie .toolbar{line-height:8px!important}.syntaxhighlighter.ie .toolbar a{padding-top:0!important}.syntaxhighlighter.printing .line.alt1 .content,.syntaxhighlighter.printing .line.alt2 .content,.syntaxhighlighter.printing .line.highlighted .number,.syntaxhighlighter.printing .line.highlighted.alt1 .content,.syntaxhighlighter.printing .line.highlighted.alt2 .content{background:none!important}.syntaxhighlighter.printing .line .number{color:#bbb!important}.syntaxhighlighter.printing .line .content{color:black!important}.syntaxhighlighter.printing .toolbar{display:none!important}.syntaxhighlighter.printing a{text-decoration:none!important}.syntaxhighlighter.printing .plain,.syntaxhighlighter.printing .plain a{color:black!important}.syntaxhighlighter.printing .comments,.syntaxhighlighter.printing .comments a{color:#008200!important}.syntaxhighlighter.printing .string,.syntaxhighlighter.printing .string a{color:blue!important}.syntaxhighlighter.printing .keyword{color:#ff7800!important;font-weight:bold!important}.syntaxhighlighter.printing .preprocessor{color:gray!important}.syntaxhighlighter.printing .variable{color:#a70!important}.syntaxhighlighter.printing .value{color:#090!important}.syntaxhighlighter.printing .functions{color:#ff1493!important}.syntaxhighlighter.printing .constants{color:#06c!important}.syntaxhighlighter.printing .script{font-weight:bold!important}.syntaxhighlighter.printing .color1,.syntaxhighlighter.printing .color1 a{color:gray!important}.syntaxhighlighter.printing .color2,.syntaxhighlighter.printing .color2 a{color:#ff1493!important}.syntaxhighlighter.printing .color3,.syntaxhighlighter.printing .color3 a{color:red!important}.syntaxhighlighter.printing .break,.syntaxhighlighter.printing .break a{color:black!important}.syntaxhighlighter{background-color:#f5f5f5!important}.syntaxhighlighter .line.highlighted.number{color:black!important}.syntaxhighlighter caption{color:black!important}.syntaxhighlighter .gutter{color:#afafaf!important;background-color:#f7f7f9!important;border-right:1px solid #e1e1e8!important;padding:9.5px 0 9.5px 9.5px!important;border-top-left-radius:4px!important;border-bottom-left-radius:4px!important;user-select:none!important;-moz-user-select:none!important;-webkit-user-select:none!important}.syntaxhighlighter .gutter .line.highlighted{background-color:#6ce26c!important;color:white!important}.syntaxhighlighter.printing .line .content{border:none!important}.syntaxhighlighter.collapsed{overflow:visible!important}.syntaxhighlighter.collapsed .toolbar{color:blue!important;background:white!important;border:1px solid #6ce26c!important}.syntaxhighlighter.collapsed .toolbar a{color:blue!important}.syntaxhighlighter.collapsed .toolbar a:hover{color:red!important}.syntaxhighlighter .toolbar{color:white!important;background:#6ce26c!important;border:none!important}.syntaxhighlighter .toolbar a{color:white!important}.syntaxhighlighter .toolbar a:hover{color:black!important}.syntaxhighlighter .plain,.syntaxhighlighter .plain a{color:black!important}.syntaxhighlighter .comments,.syntaxhighlighter .comments a{color:#008200!important}.syntaxhighlighter .string,.syntaxhighlighter .string a{color:blue!important}.syntaxhighlighter .keyword{color:#ff7800!important}.syntaxhighlighter .preprocessor{color:gray!important}.syntaxhighlighter .variable{color:#a70!important}.syntaxhighlighter .value{color:#090!important}.syntaxhighlighter .functions{color:#ff1493!important}.syntaxhighlighter .constants{color:#06c!important}.syntaxhighlighter .script{font-weight:bold!important;color:#ff7800!important;background-color:none!important}.syntaxhighlighter .color1,.syntaxhighlighter .color1 a{color:gray!important}.syntaxhighlighter .color2,.syntaxhighlighter .color2 a{color:#ff1493!important}.syntaxhighlighter .color3,.syntaxhighlighter .color3 a{color:red!important}.syntaxhighlighter .keyword{font-weight:bold!important} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | .CodeMirror { | ||
| 2 | line-height: 1em; | ||
| 3 | font-family: monospace; | ||
| 4 | } | ||
| 5 | |||
| 6 | .CodeMirror-scroll { | ||
| 7 | overflow: auto; | ||
| 8 | height: 300px; | ||
| 9 | /* This is needed to prevent an IE[67] bug where the scrolled content | ||
| 10 | is visible outside of the scrolling box. */ | ||
| 11 | position: relative; | ||
| 12 | } | ||
| 13 | |||
| 14 | .CodeMirror-gutter { | ||
| 15 | position: absolute; left: 0; top: 0; | ||
| 16 | z-index: 10; | ||
| 17 | background-color: #f7f7f7; | ||
| 18 | border-right: 1px solid #eee; | ||
| 19 | min-width: 2em; | ||
| 20 | height: 100%; | ||
| 21 | } | ||
| 22 | .CodeMirror-gutter-text { | ||
| 23 | color: #aaa; | ||
| 24 | text-align: right; | ||
| 25 | padding: .4em .2em .4em .4em; | ||
| 26 | white-space: pre !important; | ||
| 27 | } | ||
| 28 | .CodeMirror-lines { | ||
| 29 | padding: .4em; | ||
| 30 | } | ||
| 31 | |||
| 32 | .CodeMirror pre { | ||
| 33 | -moz-border-radius: 0; | ||
| 34 | -webkit-border-radius: 0; | ||
| 35 | -o-border-radius: 0; | ||
| 36 | border-radius: 0; | ||
| 37 | border-width: 0; margin: 0; padding: 0; background: transparent; | ||
| 38 | font-family: inherit; | ||
| 39 | font-size: inherit; | ||
| 40 | padding: 0; margin: 0; | ||
| 41 | white-space: pre; | ||
| 42 | word-wrap: normal; | ||
| 43 | } | ||
| 44 | |||
| 45 | .CodeMirror-wrap pre { | ||
| 46 | word-wrap: break-word; | ||
| 47 | white-space: pre-wrap; | ||
| 48 | } | ||
| 49 | .CodeMirror-wrap .CodeMirror-scroll { | ||
| 50 | overflow-x: hidden; | ||
| 51 | } | ||
| 52 | |||
| 53 | .CodeMirror textarea { | ||
| 54 | outline: none !important; | ||
| 55 | } | ||
| 56 | |||
| 57 | .CodeMirror pre.CodeMirror-cursor { | ||
| 58 | z-index: 10; | ||
| 59 | position: absolute; | ||
| 60 | visibility: hidden; | ||
| 61 | border-left: 1px solid black; | ||
| 62 | } | ||
| 63 | .CodeMirror-focused pre.CodeMirror-cursor { | ||
| 64 | visibility: visible; | ||
| 65 | } | ||
| 66 | |||
| 67 | span.CodeMirror-selected { background: #d9d9d9; } | ||
| 68 | .CodeMirror-focused span.CodeMirror-selected { background: #d2dcf8; } | ||
| 69 | |||
| 70 | .CodeMirror-searching {background: #ffa;} | ||
| 71 | |||
| 72 | /* Default theme */ | ||
| 73 | |||
| 74 | .cm-s-default span.cm-keyword {color: #708;} | ||
| 75 | .cm-s-default span.cm-atom {color: #219;} | ||
| 76 | .cm-s-default span.cm-number {color: #164;} | ||
| 77 | .cm-s-default span.cm-def {color: #00f;} | ||
| 78 | .cm-s-default span.cm-variable {color: black;} | ||
| 79 | .cm-s-default span.cm-variable-2 {color: #05a;} | ||
| 80 | .cm-s-default span.cm-variable-3 {color: #085;} | ||
| 81 | .cm-s-default span.cm-property {color: black;} | ||
| 82 | .cm-s-default span.cm-operator {color: black;} | ||
| 83 | .cm-s-default span.cm-comment {color: #a50;} | ||
| 84 | .cm-s-default span.cm-string {color: #a11;} | ||
| 85 | .cm-s-default span.cm-string-2 {color: #f50;} | ||
| 86 | .cm-s-default span.cm-meta {color: #555;} | ||
| 87 | .cm-s-default span.cm-error {color: #f00;} | ||
| 88 | .cm-s-default span.cm-qualifier {color: #555;} | ||
| 89 | .cm-s-default span.cm-builtin {color: #30a;} | ||
| 90 | .cm-s-default span.cm-bracket {color: #cc7;} | ||
| 91 | .cm-s-default span.cm-tag {color: #170;} | ||
| 92 | .cm-s-default span.cm-attribute {color: #00c;} | ||
| 93 | .cm-s-default span.cm-header {color: #a0a;} | ||
| 94 | .cm-s-default span.cm-quote {color: #090;} | ||
| 95 | .cm-s-default span.cm-hr {color: #999;} | ||
| 96 | .cm-s-default span.cm-link {color: #00c;} | ||
| 97 | |||
| 98 | span.cm-header, span.cm-strong {font-weight: bold;} | ||
| 99 | span.cm-em {font-style: italic;} | ||
| 100 | span.cm-emstrong {font-style: italic; font-weight: bold;} | ||
| 101 | span.cm-link {text-decoration: underline;} | ||
| 102 | |||
| 103 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} | ||
| 104 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} |
| 1 | /* | ||
| 2 | Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | MooTools adapter | ||
| 4 | |||
| 5 | (c) 2010-2013 Torstein Hønsi | ||
| 6 | |||
| 7 | License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | (function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this, | ||
| 10 | arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a), | ||
| 11 | g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName?$(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b=== | ||
| 12 | "unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d=null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX= | ||
| 13 | a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})(); |
| 1 | /** | ||
| 2 | * @license Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | * MooTools adapter | ||
| 4 | * | ||
| 5 | * (c) 2010-2013 Torstein Hønsi | ||
| 6 | * | ||
| 7 | * License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | |||
| 10 | // JSLint options: | ||
| 11 | /*global Fx, $, $extend, $each, $merge, Events, Event, DOMEvent */ | ||
| 12 | |||
| 13 | (function () { | ||
| 14 | |||
| 15 | var win = window, | ||
| 16 | doc = document, | ||
| 17 | mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number | ||
| 18 | legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not. | ||
| 19 | legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent. | ||
| 20 | $extend = win.$extend || function () { | ||
| 21 | return Object.append.apply(Object, arguments); | ||
| 22 | }; | ||
| 23 | |||
| 24 | win.HighchartsAdapter = { | ||
| 25 | /** | ||
| 26 | * Initialize the adapter. This is run once as Highcharts is first run. | ||
| 27 | * @param {Object} pathAnim The helper object to do animations across adapters. | ||
| 28 | */ | ||
| 29 | init: function (pathAnim) { | ||
| 30 | var fxProto = Fx.prototype, | ||
| 31 | fxStart = fxProto.start, | ||
| 32 | morphProto = Fx.Morph.prototype, | ||
| 33 | morphCompute = morphProto.compute; | ||
| 34 | |||
| 35 | // override Fx.start to allow animation of SVG element wrappers | ||
| 36 | /*jslint unparam: true*//* allow unused parameters in fx functions */ | ||
| 37 | fxProto.start = function (from, to) { | ||
| 38 | var fx = this, | ||
| 39 | elem = fx.element; | ||
| 40 | |||
| 41 | // special for animating paths | ||
| 42 | if (from.d) { | ||
| 43 | //this.fromD = this.element.d.split(' '); | ||
| 44 | fx.paths = pathAnim.init( | ||
| 45 | elem, | ||
| 46 | elem.d, | ||
| 47 | fx.toD | ||
| 48 | ); | ||
| 49 | } | ||
| 50 | fxStart.apply(fx, arguments); | ||
| 51 | |||
| 52 | return this; // chainable | ||
| 53 | }; | ||
| 54 | |||
| 55 | // override Fx.step to allow animation of SVG element wrappers | ||
| 56 | morphProto.compute = function (from, to, delta) { | ||
| 57 | var fx = this, | ||
| 58 | paths = fx.paths; | ||
| 59 | |||
| 60 | if (paths) { | ||
| 61 | fx.element.attr( | ||
| 62 | 'd', | ||
| 63 | pathAnim.step(paths[0], paths[1], delta, fx.toD) | ||
| 64 | ); | ||
| 65 | } else { | ||
| 66 | return morphCompute.apply(fx, arguments); | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | /*jslint unparam: false*/ | ||
| 70 | }, | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Run a general method on the framework, following jQuery syntax | ||
| 74 | * @param {Object} el The HTML element | ||
| 75 | * @param {String} method Which method to run on the wrapped element | ||
| 76 | */ | ||
| 77 | adapterRun: function (el, method) { | ||
| 78 | |||
| 79 | // This currently works for getting inner width and height. If adding | ||
| 80 | // more methods later, we need a conditional implementation for each. | ||
| 81 | if (method === 'width' || method === 'height') { | ||
| 82 | return parseInt($(el).getStyle(method), 10); | ||
| 83 | } | ||
| 84 | }, | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Downloads a script and executes a callback when done. | ||
| 88 | * @param {String} scriptLocation | ||
| 89 | * @param {Function} callback | ||
| 90 | */ | ||
| 91 | getScript: function (scriptLocation, callback) { | ||
| 92 | // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script. | ||
| 93 | var head = doc.getElementsByTagName('head')[0]; | ||
| 94 | var script = doc.createElement('script'); | ||
| 95 | |||
| 96 | script.type = 'text/javascript'; | ||
| 97 | script.src = scriptLocation; | ||
| 98 | script.onload = callback; | ||
| 99 | |||
| 100 | head.appendChild(script); | ||
| 101 | }, | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Animate a HTML element or SVG element wrapper | ||
| 105 | * @param {Object} el | ||
| 106 | * @param {Object} params | ||
| 107 | * @param {Object} options jQuery-like animation options: duration, easing, callback | ||
| 108 | */ | ||
| 109 | animate: function (el, params, options) { | ||
| 110 | var isSVGElement = el.attr, | ||
| 111 | effect, | ||
| 112 | complete = options && options.complete; | ||
| 113 | |||
| 114 | if (isSVGElement && !el.setStyle) { | ||
| 115 | // add setStyle and getStyle methods for internal use in Moo | ||
| 116 | el.getStyle = el.attr; | ||
| 117 | el.setStyle = function () { // property value is given as array in Moo - break it down | ||
| 118 | var args = arguments; | ||
| 119 | this.attr.call(this, args[0], args[1][0]); | ||
| 120 | }; | ||
| 121 | // dirty hack to trick Moo into handling el as an element wrapper | ||
| 122 | el.$family = function () { return true; }; | ||
| 123 | } | ||
| 124 | |||
| 125 | // stop running animations | ||
| 126 | win.HighchartsAdapter.stop(el); | ||
| 127 | |||
| 128 | // define and run the effect | ||
| 129 | effect = new Fx.Morph( | ||
| 130 | isSVGElement ? el : $(el), | ||
| 131 | $extend({ | ||
| 132 | transition: Fx.Transitions.Quad.easeInOut | ||
| 133 | }, options) | ||
| 134 | ); | ||
| 135 | |||
| 136 | // Make sure that the element reference is set when animating svg elements | ||
| 137 | if (isSVGElement) { | ||
| 138 | effect.element = el; | ||
| 139 | } | ||
| 140 | |||
| 141 | // special treatment for paths | ||
| 142 | if (params.d) { | ||
| 143 | effect.toD = params.d; | ||
| 144 | } | ||
| 145 | |||
| 146 | // jQuery-like events | ||
| 147 | if (complete) { | ||
| 148 | effect.addEvent('complete', complete); | ||
| 149 | } | ||
| 150 | |||
| 151 | // run | ||
| 152 | effect.start(params); | ||
| 153 | |||
| 154 | // record for use in stop method | ||
| 155 | el.fx = effect; | ||
| 156 | }, | ||
| 157 | |||
| 158 | /** | ||
| 159 | * MooTool's each function | ||
| 160 | * | ||
| 161 | */ | ||
| 162 | each: function (arr, fn) { | ||
| 163 | return legacy ? | ||
| 164 | $each(arr, fn) : | ||
| 165 | Array.each(arr, fn); | ||
| 166 | }, | ||
| 167 | |||
| 168 | /** | ||
| 169 | * Map an array | ||
| 170 | * @param {Array} arr | ||
| 171 | * @param {Function} fn | ||
| 172 | */ | ||
| 173 | map: function (arr, fn) { | ||
| 174 | return arr.map(fn); | ||
| 175 | }, | ||
| 176 | |||
| 177 | /** | ||
| 178 | * Grep or filter an array | ||
| 179 | * @param {Array} arr | ||
| 180 | * @param {Function} fn | ||
| 181 | */ | ||
| 182 | grep: function (arr, fn) { | ||
| 183 | return arr.filter(fn); | ||
| 184 | }, | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Return the index of an item in an array, or -1 if not matched | ||
| 188 | */ | ||
| 189 | inArray: function (item, arr, from) { | ||
| 190 | return arr ? arr.indexOf(item, from) : -1; | ||
| 191 | }, | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Get the offset of an element relative to the top left corner of the web page | ||
| 195 | */ | ||
| 196 | offset: function (el) { | ||
| 197 | var offsets = el.getPosition(); // #1496 | ||
| 198 | return { | ||
| 199 | left: offsets.x, | ||
| 200 | top: offsets.y | ||
| 201 | }; | ||
| 202 | }, | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Extends an object with Events, if its not done | ||
| 206 | */ | ||
| 207 | extendWithEvents: function (el) { | ||
| 208 | // if the addEvent method is not defined, el is a custom Highcharts object | ||
| 209 | // like series or point | ||
| 210 | if (!el.addEvent) { | ||
| 211 | if (el.nodeName) { | ||
| 212 | el = $(el); // a dynamically generated node | ||
| 213 | } else { | ||
| 214 | $extend(el, new Events()); // a custom object | ||
| 215 | } | ||
| 216 | } | ||
| 217 | }, | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Add an event listener | ||
| 221 | * @param {Object} el HTML element or custom object | ||
| 222 | * @param {String} type Event type | ||
| 223 | * @param {Function} fn Event handler | ||
| 224 | */ | ||
| 225 | addEvent: function (el, type, fn) { | ||
| 226 | if (typeof type === 'string') { // chart broke due to el being string, type function | ||
| 227 | |||
| 228 | if (type === 'unload') { // Moo self destructs before custom unload events | ||
| 229 | type = 'beforeunload'; | ||
| 230 | } | ||
| 231 | |||
| 232 | win.HighchartsAdapter.extendWithEvents(el); | ||
| 233 | |||
| 234 | el.addEvent(type, fn); | ||
| 235 | } | ||
| 236 | }, | ||
| 237 | |||
| 238 | removeEvent: function (el, type, fn) { | ||
| 239 | if (typeof el === 'string') { | ||
| 240 | // el.removeEvents below apperantly calls this method again. Do not quite understand why, so for now just bail out. | ||
| 241 | return; | ||
| 242 | } | ||
| 243 | |||
| 244 | if (el.addEvent) { // If el doesn't have an addEvent method, there are no events to remove | ||
| 245 | if (type) { | ||
| 246 | if (type === 'unload') { // Moo self destructs before custom unload events | ||
| 247 | type = 'beforeunload'; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (fn) { | ||
| 251 | el.removeEvent(type, fn); | ||
| 252 | } else if (el.removeEvents) { // #958 | ||
| 253 | el.removeEvents(type); | ||
| 254 | } | ||
| 255 | } else { | ||
| 256 | el.removeEvents(); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | }, | ||
| 260 | |||
| 261 | fireEvent: function (el, event, eventArguments, defaultFunction) { | ||
| 262 | var eventArgs = { | ||
| 263 | type: event, | ||
| 264 | target: el | ||
| 265 | }; | ||
| 266 | // create an event object that keeps all functions | ||
| 267 | event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs); | ||
| 268 | event = $extend(event, eventArguments); | ||
| 269 | |||
| 270 | // When running an event on the Chart.prototype, MooTools nests the target in event.event | ||
| 271 | if (!event.target && event.event) { | ||
| 272 | event.target = event.event.target; | ||
| 273 | } | ||
| 274 | |||
| 275 | // override the preventDefault function to be able to use | ||
| 276 | // this for custom events | ||
| 277 | event.preventDefault = function () { | ||
| 278 | defaultFunction = null; | ||
| 279 | }; | ||
| 280 | // if fireEvent is not available on the object, there hasn't been added | ||
| 281 | // any events to it above | ||
| 282 | if (el.fireEvent) { | ||
| 283 | el.fireEvent(event.type, event); | ||
| 284 | } | ||
| 285 | |||
| 286 | // fire the default if it is passed and it is not prevented above | ||
| 287 | if (defaultFunction) { | ||
| 288 | defaultFunction(event); | ||
| 289 | } | ||
| 290 | }, | ||
| 291 | |||
| 292 | /** | ||
| 293 | * Set back e.pageX and e.pageY that MooTools has abstracted away. #1165, #1346. | ||
| 294 | */ | ||
| 295 | washMouseEvent: function (e) { | ||
| 296 | if (e.page) { | ||
| 297 | e.pageX = e.page.x; | ||
| 298 | e.pageY = e.page.y; | ||
| 299 | } | ||
| 300 | return e; | ||
| 301 | }, | ||
| 302 | |||
| 303 | /** | ||
| 304 | * Stop running animations on the object | ||
| 305 | */ | ||
| 306 | stop: function (el) { | ||
| 307 | if (el.fx) { | ||
| 308 | el.fx.cancel(); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | }; | ||
| 312 | |||
| 313 | }()); |
| 1 | /* | ||
| 2 | Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | Prototype adapter | ||
| 4 | |||
| 5 | @author Michael Nelson, Torstein Hønsi. | ||
| 6 | |||
| 7 | Feel free to use and modify this script. | ||
| 8 | Highcharts license: www.highcharts.com/license. | ||
| 9 | */ | ||
| 10 | var HighchartsAdapter=function(){var f=typeof Effect!=="undefined";return{init:function(a){if(f)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(b,c,d,g){var e;this.element=b;this.key=c;e=b.attr?b.attr(c):$(b).getStyle(c);if(c==="d")this.paths=a.init(b,b.d,d),this.toD=d,e=0,d=1;this.start(Object.extend(g||{},{from:e,to:d,attribute:c}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]= | ||
| 11 | this},update:function(b){var c=this.paths,d=this.element;c&&(b=a.step(c[0],c[1],b,this.toD));d.attr?d.element&&d.attr(this.options.attribute,b):(c={},c[this.options.attribute]=b,$(d).setStyle(c))},finish:function(){this.element&&this.element._highchart_animation&&delete this.element._highchart_animation[this.key]}})},adapterRun:function(a,b){return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=$$("head")[0];c&&c.appendChild((new Element("script",{type:"text/javascript",src:a})).observe("load", | ||
| 12 | b))},addNS:function(a){var b=/^(?:click|mouse(?:down|up|over|move|out))$/;return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(a)||b.test(a)?a:"h:"+a},addEvent:function(a,b,c){a.addEventListener||a.attachEvent?Event.observe($(a),HighchartsAdapter.addNS(b),c):(HighchartsAdapter._extend(a),a._highcharts_observe(b,c))},animate:function(a,b,c){var d,c=c||{};c.delay=0;c.duration=(c.duration||500)/1E3;c.afterFinish=c.complete;if(f)for(d in b)new Effect.HighchartsTransition($(a), | ||
| 13 | d,b[d],c);else{if(a.attr)for(d in b)a.attr(d,b[d]);c.complete&&c.complete()}a.attr||$(a).setStyle(b)},stop:function(a){var b;if(a._highcharts_extended&&a._highchart_animation)for(b in a._highchart_animation)a._highchart_animation[b].cancel()},each:function(a,b){$A(a).each(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){return $(a).cumulativeOffset()},fireEvent:function(a,b,c,d){a.fire?a.fire(HighchartsAdapter.addNS(b),c):a._highcharts_extended&&(c=c||{},a._highcharts_fire(b, | ||
| 14 | c));c&&c.defaultPrevented&&(d=null);d&&d(c)},removeEvent:function(a,b,c){$(a).stopObserving&&(b&&(b=HighchartsAdapter.addNS(b)),$(a).stopObserving(b,c));window===a?Event.stopObserving(a,b,c):(HighchartsAdapter._extend(a),a._highcharts_stop_observing(b,c))},washMouseEvent:function(a){return a},grep:function(a,b){return a.findAll(b)},map:function(a,b){return a.map(b)},_extend:function(a){a._highcharts_extended||Object.extend(a,{_highchart_events:{},_highchart_animation:null,_highcharts_extended:!0, | ||
| 15 | _highcharts_observe:function(b,a){this._highchart_events[b]=[this._highchart_events[b],a].compact().flatten()},_highcharts_stop_observing:function(b,a){b?a?this._highchart_events[b]=[this._highchart_events[b]].compact().flatten().without(a):delete this._highchart_events[b]:this._highchart_events={}},_highcharts_fire:function(a,c){var d=this;(this._highchart_events[a]||[]).each(function(a){if(!c.stopped)c.preventDefault=function(){c.defaultPrevented=!0},c.target=d,a.bind(this)(c)===!1&&c.preventDefault()}.bind(this))}})}}}(); |
| 1 | /** | ||
| 2 | * @license Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | * Prototype adapter | ||
| 4 | * | ||
| 5 | * @author Michael Nelson, Torstein Hønsi. | ||
| 6 | * | ||
| 7 | * Feel free to use and modify this script. | ||
| 8 | * Highcharts license: www.highcharts.com/license. | ||
| 9 | */ | ||
| 10 | |||
| 11 | // JSLint options: | ||
| 12 | /*global Effect, Class, Event, Element, $, $$, $A */ | ||
| 13 | |||
| 14 | // Adapter interface between prototype and the Highcharts charting library | ||
| 15 | var HighchartsAdapter = (function () { | ||
| 16 | |||
| 17 | var hasEffect = typeof Effect !== 'undefined'; | ||
| 18 | |||
| 19 | return { | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Initialize the adapter. This is run once as Highcharts is first run. | ||
| 23 | * @param {Object} pathAnim The helper object to do animations across adapters. | ||
| 24 | */ | ||
| 25 | init: function (pathAnim) { | ||
| 26 | if (hasEffect) { | ||
| 27 | /** | ||
| 28 | * Animation for Highcharts SVG element wrappers only | ||
| 29 | * @param {Object} element | ||
| 30 | * @param {Object} attribute | ||
| 31 | * @param {Object} to | ||
| 32 | * @param {Object} options | ||
| 33 | */ | ||
| 34 | Effect.HighchartsTransition = Class.create(Effect.Base, { | ||
| 35 | initialize: function (element, attr, to, options) { | ||
| 36 | var from, | ||
| 37 | opts; | ||
| 38 | |||
| 39 | this.element = element; | ||
| 40 | this.key = attr; | ||
| 41 | from = element.attr ? element.attr(attr) : $(element).getStyle(attr); | ||
| 42 | |||
| 43 | // special treatment for paths | ||
| 44 | if (attr === 'd') { | ||
| 45 | this.paths = pathAnim.init( | ||
| 46 | element, | ||
| 47 | element.d, | ||
| 48 | to | ||
| 49 | ); | ||
| 50 | this.toD = to; | ||
| 51 | |||
| 52 | |||
| 53 | // fake values in order to read relative position as a float in update | ||
| 54 | from = 0; | ||
| 55 | to = 1; | ||
| 56 | } | ||
| 57 | |||
| 58 | opts = Object.extend((options || {}), { | ||
| 59 | from: from, | ||
| 60 | to: to, | ||
| 61 | attribute: attr | ||
| 62 | }); | ||
| 63 | this.start(opts); | ||
| 64 | }, | ||
| 65 | setup: function () { | ||
| 66 | HighchartsAdapter._extend(this.element); | ||
| 67 | // If this is the first animation on this object, create the _highcharts_animation helper that | ||
| 68 | // contain pointers to the animation objects. | ||
| 69 | if (!this.element._highchart_animation) { | ||
| 70 | this.element._highchart_animation = {}; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Store a reference to this animation instance. | ||
| 74 | this.element._highchart_animation[this.key] = this; | ||
| 75 | }, | ||
| 76 | update: function (position) { | ||
| 77 | var paths = this.paths, | ||
| 78 | element = this.element, | ||
| 79 | obj; | ||
| 80 | |||
| 81 | if (paths) { | ||
| 82 | position = pathAnim.step(paths[0], paths[1], position, this.toD); | ||
| 83 | } | ||
| 84 | |||
| 85 | if (element.attr) { // SVGElement | ||
| 86 | |||
| 87 | if (element.element) { // If not, it has been destroyed (#1405) | ||
| 88 | element.attr(this.options.attribute, position); | ||
| 89 | } | ||
| 90 | |||
| 91 | } else { // HTML, #409 | ||
| 92 | obj = {}; | ||
| 93 | obj[this.options.attribute] = position; | ||
| 94 | $(element).setStyle(obj); | ||
| 95 | } | ||
| 96 | |||
| 97 | }, | ||
| 98 | finish: function () { | ||
| 99 | // Delete the property that holds this animation now that it is finished. | ||
| 100 | // Both canceled animations and complete ones gets a 'finish' call. | ||
| 101 | if (this.element && this.element._highchart_animation) { // #1405 | ||
| 102 | delete this.element._highchart_animation[this.key]; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | }); | ||
| 106 | } | ||
| 107 | }, | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Run a general method on the framework, following jQuery syntax | ||
| 111 | * @param {Object} el The HTML element | ||
| 112 | * @param {String} method Which method to run on the wrapped element | ||
| 113 | */ | ||
| 114 | adapterRun: function (el, method) { | ||
| 115 | |||
| 116 | // This currently works for getting inner width and height. If adding | ||
| 117 | // more methods later, we need a conditional implementation for each. | ||
| 118 | return parseInt($(el).getStyle(method), 10); | ||
| 119 | |||
| 120 | }, | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Downloads a script and executes a callback when done. | ||
| 124 | * @param {String} scriptLocation | ||
| 125 | * @param {Function} callback | ||
| 126 | */ | ||
| 127 | getScript: function (scriptLocation, callback) { | ||
| 128 | var head = $$('head')[0]; // Returns an array, so pick the first element. | ||
| 129 | if (head) { | ||
| 130 | // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback | ||
| 131 | head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback)); | ||
| 132 | } | ||
| 133 | }, | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of | ||
| 137 | * events that are not recognized as native. | ||
| 138 | */ | ||
| 139 | addNS: function (eventName) { | ||
| 140 | var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, | ||
| 141 | MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/; | ||
| 142 | return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ? | ||
| 143 | eventName : | ||
| 144 | 'h:' + eventName; | ||
| 145 | }, | ||
| 146 | |||
| 147 | // el needs an event to be attached. el is not necessarily a dom element | ||
| 148 | addEvent: function (el, event, fn) { | ||
| 149 | if (el.addEventListener || el.attachEvent) { | ||
| 150 | Event.observe($(el), HighchartsAdapter.addNS(event), fn); | ||
| 151 | |||
| 152 | } else { | ||
| 153 | HighchartsAdapter._extend(el); | ||
| 154 | el._highcharts_observe(event, fn); | ||
| 155 | } | ||
| 156 | }, | ||
| 157 | |||
| 158 | // motion makes things pretty. use it if effects is loaded, if not... still get to the end result. | ||
| 159 | animate: function (el, params, options) { | ||
| 160 | var key, | ||
| 161 | fx; | ||
| 162 | |||
| 163 | // default options | ||
| 164 | options = options || {}; | ||
| 165 | options.delay = 0; | ||
| 166 | options.duration = (options.duration || 500) / 1000; | ||
| 167 | options.afterFinish = options.complete; | ||
| 168 | |||
| 169 | // animate wrappers and DOM elements | ||
| 170 | if (hasEffect) { | ||
| 171 | for (key in params) { | ||
| 172 | // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object | ||
| 173 | // on the element itself so its not really lost. | ||
| 174 | fx = new Effect.HighchartsTransition($(el), key, params[key], options); | ||
| 175 | } | ||
| 176 | } else { | ||
| 177 | if (el.attr) { // #409 without effects | ||
| 178 | for (key in params) { | ||
| 179 | el.attr(key, params[key]); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | if (options.complete) { | ||
| 183 | options.complete(); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | if (!el.attr) { // HTML element, #409 | ||
| 188 | $(el).setStyle(params); | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | |||
| 192 | // this only occurs in higcharts 2.0+ | ||
| 193 | stop: function (el) { | ||
| 194 | var key; | ||
| 195 | if (el._highcharts_extended && el._highchart_animation) { | ||
| 196 | for (key in el._highchart_animation) { | ||
| 197 | // Cancel the animation | ||
| 198 | // The 'finish' function in the Effect object will remove the reference | ||
| 199 | el._highchart_animation[key].cancel(); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | }, | ||
| 203 | |||
| 204 | // um.. each | ||
| 205 | each: function (arr, fn) { | ||
| 206 | $A(arr).each(fn); | ||
| 207 | }, | ||
| 208 | |||
| 209 | inArray: function (item, arr, from) { | ||
| 210 | return arr ? arr.indexOf(item, from) : -1; | ||
| 211 | }, | ||
| 212 | |||
| 213 | /** | ||
| 214 | * Get the cumulative offset relative to the top left of the page. This method, unlike its | ||
| 215 | * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position | ||
| 216 | * of a chart within a fixed container. | ||
| 217 | */ | ||
| 218 | offset: function (el) { | ||
| 219 | return $(el).cumulativeOffset(); | ||
| 220 | }, | ||
| 221 | |||
| 222 | // fire an event based on an event name (event) and an object (el). | ||
| 223 | // again, el may not be a dom element | ||
| 224 | fireEvent: function (el, event, eventArguments, defaultFunction) { | ||
| 225 | if (el.fire) { | ||
| 226 | el.fire(HighchartsAdapter.addNS(event), eventArguments); | ||
| 227 | } else if (el._highcharts_extended) { | ||
| 228 | eventArguments = eventArguments || {}; | ||
| 229 | el._highcharts_fire(event, eventArguments); | ||
| 230 | } | ||
| 231 | |||
| 232 | if (eventArguments && eventArguments.defaultPrevented) { | ||
| 233 | defaultFunction = null; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (defaultFunction) { | ||
| 237 | defaultFunction(eventArguments); | ||
| 238 | } | ||
| 239 | }, | ||
| 240 | |||
| 241 | removeEvent: function (el, event, handler) { | ||
| 242 | if ($(el).stopObserving) { | ||
| 243 | if (event) { | ||
| 244 | event = HighchartsAdapter.addNS(event); | ||
| 245 | } | ||
| 246 | $(el).stopObserving(event, handler); | ||
| 247 | } if (window === el) { | ||
| 248 | Event.stopObserving(el, event, handler); | ||
| 249 | } else { | ||
| 250 | HighchartsAdapter._extend(el); | ||
| 251 | el._highcharts_stop_observing(event, handler); | ||
| 252 | } | ||
| 253 | }, | ||
| 254 | |||
| 255 | washMouseEvent: function (e) { | ||
| 256 | return e; | ||
| 257 | }, | ||
| 258 | |||
| 259 | // um, grep | ||
| 260 | grep: function (arr, fn) { | ||
| 261 | return arr.findAll(fn); | ||
| 262 | }, | ||
| 263 | |||
| 264 | // um, map | ||
| 265 | map: function (arr, fn) { | ||
| 266 | return arr.map(fn); | ||
| 267 | }, | ||
| 268 | |||
| 269 | // extend an object to handle highchart events (highchart objects, not svg elements). | ||
| 270 | // this is a very simple way of handling events but whatever, it works (i think) | ||
| 271 | _extend: function (object) { | ||
| 272 | if (!object._highcharts_extended) { | ||
| 273 | Object.extend(object, { | ||
| 274 | _highchart_events: {}, | ||
| 275 | _highchart_animation: null, | ||
| 276 | _highcharts_extended: true, | ||
| 277 | _highcharts_observe: function (name, fn) { | ||
| 278 | this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten(); | ||
| 279 | }, | ||
| 280 | _highcharts_stop_observing: function (name, fn) { | ||
| 281 | if (name) { | ||
| 282 | if (fn) { | ||
| 283 | this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn); | ||
| 284 | } else { | ||
| 285 | delete this._highchart_events[name]; | ||
| 286 | } | ||
| 287 | } else { | ||
| 288 | this._highchart_events = {}; | ||
| 289 | } | ||
| 290 | }, | ||
| 291 | _highcharts_fire: function (name, args) { | ||
| 292 | var target = this; | ||
| 293 | (this._highchart_events[name] || []).each(function (fn) { | ||
| 294 | // args is never null here | ||
| 295 | if (args.stopped) { | ||
| 296 | return; // "throw $break" wasn't working. i think because of the scope of 'this'. | ||
| 297 | } | ||
| 298 | |||
| 299 | // Attach a simple preventDefault function to skip default handler if called | ||
| 300 | args.preventDefault = function () { | ||
| 301 | args.defaultPrevented = true; | ||
| 302 | }; | ||
| 303 | args.target = target; | ||
| 304 | |||
| 305 | // If the event handler return false, prevent the default handler from executing | ||
| 306 | if (fn.bind(this)(args) === false) { | ||
| 307 | args.preventDefault(); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | .bind(this)); | ||
| 311 | } | ||
| 312 | }); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | }; | ||
| 316 | }()); |
| 1 | /* | ||
| 2 | Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | |||
| 4 | Standalone Highcharts Framework | ||
| 5 | |||
| 6 | License: MIT License | ||
| 7 | */ | ||
| 8 | var HighchartsAdapter=function(){function o(c){function a(a,b,d){a.removeEventListener(b,d,!1)}function d(a,b,d){d=a.HCProxiedMethods[d.toString()];a.detachEvent("on"+b,d)}function b(b,c){var f=b.HCEvents,i,g,k,j;if(b.removeEventListener)i=a;else if(b.attachEvent)i=d;else return;c?(g={},g[c]=!0):g=f;for(j in g)if(f[j])for(k=f[j].length;k--;)i(b,j,f[j][k])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(b,a){var d=this,c=this.HCEvents,g;if(d.addEventListener)d.addEventListener(b, | ||
| 9 | a,!1);else if(d.attachEvent){g=function(b){a.call(d,b)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[a.toString()]=g;d.attachEvent("on"+b,g)}c[b]===r&&(c[b]=[]);c[b].push(a)},unbind:function(c,h){var f,i;c?(f=this.HCEvents[c]||[],h?(i=HighchartsAdapter.inArray(h,f),i>-1&&(f.splice(i,1),this.HCEvents[c]=f),this.removeEventListener?a(this,c,h):this.attachEvent&&d(this,c,h)):(b(this,c),this.HCEvents[c]=[])):(b(this),this.HCEvents={})},trigger:function(b,a){var d=this.HCEvents[b]|| | ||
| 10 | [],c=d.length,g,k,j;k=function(){a.defaultPrevented=!0};for(g=0;g<c;g++){j=d[g];if(a.stopped)break;a.preventDefault=k;a.target=this;a.type=b;j.call(this,a)===!1&&a.preventDefault()}}});return c}var r,l=document,p=[],m=[],q,n;Math.easeInOutSine=function(c,a,d,b){return-d/2*(Math.cos(Math.PI*c/b)-1)+a};return{init:function(c){if(!l.defaultView)this._getStyle=function(a,d){var b;return a.style[d]?a.style[d]:(d==="opacity"&&(d="filter"),b=a.currentStyle[d.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()})], | ||
| 11 | d==="filter"&&(b=b.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),b===""?1:b)},this.adapterRun=function(a,d){var b={width:"clientWidth",height:"clientHeight"}[d];if(b)return a.style.zoom=1,a[b]-2*parseInt(HighchartsAdapter._getStyle(a,"padding"),10)};if(!Array.prototype.forEach)this.each=function(a,d){for(var b=0,c=a.length;b<c;b++)if(d.call(a[b],a[b],b,a)===!1)return b};if(!Array.prototype.indexOf)this.inArray=function(a,d){var b,c=0;if(d)for(b=d.length;c<b;c++)if(d[c]===a)return c; | ||
| 12 | return-1};if(!Array.prototype.filter)this.grep=function(a,d){for(var b=[],c=0,h=a.length;c<h;c++)d(a[c],c)&&b.push(a[c]);return b};n=function(a,c,b){this.options=c;this.elem=a;this.prop=b};n.prototype={update:function(){var a;a=this.paths;var d=this.elem,b=d.element;a&&b?d.attr("d",c.step(a[0],a[1],this.now,this.toD)):d.attr?b&&d.attr(this.prop,this.now):(a={},a[d]=this.now+this.unit,Highcharts.css(d,a));this.options.step&&this.options.step.call(this.elem,this.now,this)},custom:function(a,c,b){var e= | ||
| 13 | this,h=function(a){return e.step(a)},f;this.startTime=+new Date;this.start=a;this.end=c;this.unit=b;this.now=this.start;this.pos=this.state=0;h.elem=this.elem;h()&&m.push(h)===1&&(q=setInterval(function(){for(f=0;f<m.length;f++)m[f]()||m.splice(f--,1);m.length||clearInterval(q)},13))},step:function(a){var c=+new Date,b;b=this.options;var e;if(this.elem.stopAnimation)b=!1;else if(a||c>=b.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();a=this.options.curAnim[this.prop]= | ||
| 14 | !0;for(e in b.curAnim)b.curAnim[e]!==!0&&(a=!1);a&&b.complete&&b.complete.call(this.elem);b=!1}else e=c-this.startTime,this.state=e/b.duration,this.pos=b.easing(e,0,1,b.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),b=!0;return b}};this.animate=function(a,d,b){var e,h="",f,i,g;a.stopAnimation=!1;if(typeof b!=="object"||b===null)e=arguments,b={duration:e[2],easing:e[3],complete:e[4]};if(typeof b.duration!=="number")b.duration=400;b.easing=Math[b.easing]||Math.easeInOutSine; | ||
| 15 | b.curAnim=Highcharts.extend({},d);for(g in d)i=new n(a,b,g),f=null,g==="d"?(i.paths=c.init(a,a.d,d.d),i.toD=d.d,e=0,f=1):a.attr?e=a.attr(g):(e=parseFloat(HighchartsAdapter._getStyle(a,g))||0,g!=="opacity"&&(h="px")),f||(f=parseFloat(d[g])),i.custom(e,f,h)}},_getStyle:function(c,a){return window.getComputedStyle(c).getPropertyValue(a)},getScript:function(c,a){var d=l.getElementsByTagName("head")[0],b=l.createElement("script");b.type="text/javascript";b.src=c;b.onload=a;d.appendChild(b)},inArray:function(c, | ||
| 16 | a){return a.indexOf?a.indexOf(c):p.indexOf.call(a,c)},adapterRun:function(c,a){return parseInt(HighchartsAdapter._getStyle(c,a),10)},grep:function(c,a){return p.filter.call(c,a)},map:function(c,a){for(var d=[],b=0,e=c.length;b<e;b++)d[b]=a.call(c[b],c[b],b,c);return d},offset:function(c){for(var a=0,d=0;c;)a+=c.offsetLeft,d+=c.offsetTop,c=c.offsetParent;return{left:a,top:d}},addEvent:function(c,a,d){o(c).bind(a,d)},removeEvent:function(c,a,d){o(c).unbind(a,d)},fireEvent:function(c,a,d,b){var e;l.createEvent&& | ||
| 17 | (c.dispatchEvent||c.fireEvent)?(e=l.createEvent("Events"),e.initEvent(a,!0,!0),e.target=c,Highcharts.extend(e,d),c.dispatchEvent?c.dispatchEvent(e):c.fireEvent(a,e)):c.HCExtended===!0&&(d=d||{},c.trigger(a,d));d&&d.defaultPrevented&&(b=null);b&&b(d)},washMouseEvent:function(c){return c},stop:function(c){c.stopAnimation=!0},each:function(c,a){return Array.prototype.forEach.call(c,a)}}}(); |
| 1 | (function(i,C){function m(a){return typeof a==="number"}function n(a){return a!==D&&a!==null}var D,p,r,s=i.Chart,t=i.extend,z=i.each;r=["path","rect","circle"];p={top:0,left:0,center:0.5,middle:0.5,bottom:1,right:1};var u=C.inArray,A=i.merge,B=function(){this.init.apply(this,arguments)};B.prototype={init:function(a,d){var c=d.shape&&d.shape.type;this.chart=a;var b,f;f={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",fill:"transparent",strokeWidth:2}}};b={circle:{params:{x:0, | ||
| 2 | y:0}}};if(b[c])f.shape=A(f.shape,b[c]);this.options=A({},f,d)},render:function(a){var d=this.chart,c=this.chart.renderer,b=this.group,f=this.title,e=this.shape,h=this.options,i=h.title,l=h.shape;if(!b)b=this.group=c.g();if(!e&&l&&u(l.type,r)!==-1)e=this.shape=c[h.shape.type](l.params),e.add(b);if(!f&&i)f=this.title=c.label(i),f.add(b);b.add(d.annotations.group);this.linkObjects();a!==!1&&this.redraw()},redraw:function(){var a=this.options,d=this.chart,c=this.group,b=this.title,f=this.shape,e=this.linkedObject, | ||
| 3 | h=d.xAxis[a.xAxis],v=d.yAxis[a.yAxis],l=a.width,w=a.height,x=p[a.anchorY],y=p[a.anchorX],j,o,g,q;if(e)j=e instanceof i.Point?"point":e instanceof i.Series?"series":null,j==="point"?(a.xValue=e.x,a.yValue=e.y,o=e.series):j==="series"&&(o=e),c.visibility!==o.group.visibility&&c.attr({visibility:o.group.visibility});e=n(a.xValue)?h.toPixels(a.xValue+h.minPointOffset)-h.minPixelPadding:a.x;j=n(a.yValue)?v.toPixels(a.yValue):a.y;if(!isNaN(e)&&!isNaN(j)&&m(e)&&m(j)){b&&(b.attr(a.title),b.css(a.title.style)); | ||
| 4 | if(f){b=t({},a.shape.params);if(a.units==="values"){for(g in b)u(g,["width","x"])>-1?b[g]=h.translate(b[g]):u(g,["height","y"])>-1&&(b[g]=v.translate(b[g]));b.width&&(b.width-=h.toPixels(0)-h.left);b.x&&(b.x+=h.minPixelPadding);if(a.shape.type==="path"){g=b.d;o=e;for(var r=j,s=g.length,k=0;k<s;)typeof g[k]==="number"&&typeof g[k+1]==="number"?(g[k]=h.toPixels(g[k])-o,g[k+1]=v.toPixels(g[k+1])-r,k+=2):k+=1}}a.shape.type==="circle"&&(b.x+=b.r,b.y+=b.r);f.attr(b)}c.bBox=null;if(!m(l))q=c.getBBox(),l= | ||
| 5 | q.width;if(!m(w))q||(q=c.getBBox()),w=q.height;if(!m(y))y=p.center;if(!m(x))x=p.center;e-=l*y;j-=w*x;d.animation&&n(c.translateX)&&n(c.translateY)?c.animate({translateX:e,translateY:j}):c.translate(e,j)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);c>-1&&d.splice(c,1);z(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){t(this.options,a);this.linkObjects();this.render(d)}, | ||
| 6 | linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;if(n(b)){if(!n(d)||b!==c)this.linkedObject=a.get(b)}else this.linkedObject=null}};t(s.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;Object.prototype.toString.call(a)==="[object Array]"||(a=[a]);for(e=a.length;e--;)f=new B(b,a[e]),c.push(f),f.render(d)},redraw:function(){z(this.allItems,function(a){a.redraw()})}}});s.prototype.callbacks.push(function(a){var d= | ||
| 7 | a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;Object.prototype.toString.call(d)==="[object Array]"&&d.length>0&&a.annotations.add(a.options.annotations);i.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(Highcharts,HighchartsAdapter); |
| 1 | (function (Highcharts, HighchartsAdapter) { | ||
| 2 | |||
| 3 | var UNDEFINED, | ||
| 4 | ALIGN_FACTOR, | ||
| 5 | ALLOWED_SHAPES, | ||
| 6 | Chart = Highcharts.Chart, | ||
| 7 | extend = Highcharts.extend, | ||
| 8 | each = Highcharts.each; | ||
| 9 | |||
| 10 | ALLOWED_SHAPES = ["path", "rect", "circle"]; | ||
| 11 | |||
| 12 | ALIGN_FACTOR = { | ||
| 13 | top: 0, | ||
| 14 | left: 0, | ||
| 15 | center: 0.5, | ||
| 16 | middle: 0.5, | ||
| 17 | bottom: 1, | ||
| 18 | right: 1 | ||
| 19 | }; | ||
| 20 | |||
| 21 | |||
| 22 | // Highcharts helper methods | ||
| 23 | var inArray = HighchartsAdapter.inArray, | ||
| 24 | merge = Highcharts.merge; | ||
| 25 | |||
| 26 | function defaultOptions(shapeType) { | ||
| 27 | var shapeOptions, | ||
| 28 | options; | ||
| 29 | |||
| 30 | options = { | ||
| 31 | xAxis: 0, | ||
| 32 | yAxis: 0, | ||
| 33 | title: { | ||
| 34 | style: {}, | ||
| 35 | text: "", | ||
| 36 | x: 0, | ||
| 37 | y: 0 | ||
| 38 | }, | ||
| 39 | shape: { | ||
| 40 | params: { | ||
| 41 | stroke: "#000000", | ||
| 42 | fill: "transparent", | ||
| 43 | strokeWidth: 2 | ||
| 44 | } | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | shapeOptions = { | ||
| 49 | circle: { | ||
| 50 | params: { | ||
| 51 | x: 0, | ||
| 52 | y: 0 | ||
| 53 | } | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | |||
| 57 | if (shapeOptions[shapeType]) { | ||
| 58 | options.shape = merge(options.shape, shapeOptions[shapeType]); | ||
| 59 | } | ||
| 60 | |||
| 61 | return options; | ||
| 62 | } | ||
| 63 | |||
| 64 | function isArray(obj) { | ||
| 65 | return Object.prototype.toString.call(obj) === '[object Array]'; | ||
| 66 | } | ||
| 67 | |||
| 68 | function isNumber(n) { | ||
| 69 | return typeof n === 'number'; | ||
| 70 | } | ||
| 71 | |||
| 72 | function defined(obj) { | ||
| 73 | return obj !== UNDEFINED && obj !== null; | ||
| 74 | } | ||
| 75 | |||
| 76 | function translatePath(d, xAxis, yAxis, xOffset, yOffset) { | ||
| 77 | var len = d.length, | ||
| 78 | i = 0; | ||
| 79 | |||
| 80 | while (i < len) { | ||
| 81 | if (typeof d[i] === 'number' && typeof d[i + 1] === 'number') { | ||
| 82 | d[i] = xAxis.toPixels(d[i]) - xOffset; | ||
| 83 | d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset; | ||
| 84 | i += 2; | ||
| 85 | } else { | ||
| 86 | i += 1; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | return d; | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | // Define annotation prototype | ||
| 95 | var Annotation = function () { | ||
| 96 | this.init.apply(this, arguments); | ||
| 97 | }; | ||
| 98 | Annotation.prototype = { | ||
| 99 | /* | ||
| 100 | * Initialize the annotation | ||
| 101 | */ | ||
| 102 | init: function (chart, options) { | ||
| 103 | var shapeType = options.shape && options.shape.type; | ||
| 104 | |||
| 105 | this.chart = chart; | ||
| 106 | this.options = merge({}, defaultOptions(shapeType), options); | ||
| 107 | }, | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Render the annotation | ||
| 111 | */ | ||
| 112 | render: function (redraw) { | ||
| 113 | var annotation = this, | ||
| 114 | chart = this.chart, | ||
| 115 | renderer = annotation.chart.renderer, | ||
| 116 | group = annotation.group, | ||
| 117 | title = annotation.title, | ||
| 118 | shape = annotation.shape, | ||
| 119 | options = annotation.options, | ||
| 120 | titleOptions = options.title, | ||
| 121 | shapeOptions = options.shape; | ||
| 122 | |||
| 123 | if (!group) { | ||
| 124 | group = annotation.group = renderer.g(); | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { | ||
| 129 | shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); | ||
| 130 | shape.add(group); | ||
| 131 | } | ||
| 132 | |||
| 133 | if (!title && titleOptions) { | ||
| 134 | title = annotation.title = renderer.label(titleOptions); | ||
| 135 | title.add(group); | ||
| 136 | } | ||
| 137 | |||
| 138 | group.add(chart.annotations.group); | ||
| 139 | |||
| 140 | // link annotations to point or series | ||
| 141 | annotation.linkObjects(); | ||
| 142 | |||
| 143 | if (redraw !== false) { | ||
| 144 | annotation.redraw(); | ||
| 145 | } | ||
| 146 | }, | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Redraw the annotation title or shape after options update | ||
| 150 | */ | ||
| 151 | redraw: function () { | ||
| 152 | var options = this.options, | ||
| 153 | chart = this.chart, | ||
| 154 | group = this.group, | ||
| 155 | title = this.title, | ||
| 156 | shape = this.shape, | ||
| 157 | linkedTo = this.linkedObject, | ||
| 158 | xAxis = chart.xAxis[options.xAxis], | ||
| 159 | yAxis = chart.yAxis[options.yAxis], | ||
| 160 | width = options.width, | ||
| 161 | height = options.height, | ||
| 162 | anchorY = ALIGN_FACTOR[options.anchorY], | ||
| 163 | anchorX = ALIGN_FACTOR[options.anchorX], | ||
| 164 | resetBBox = false, | ||
| 165 | shapeParams, | ||
| 166 | linkType, | ||
| 167 | series, | ||
| 168 | param, | ||
| 169 | bbox, | ||
| 170 | x, | ||
| 171 | y; | ||
| 172 | |||
| 173 | if (linkedTo) { | ||
| 174 | linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : | ||
| 175 | (linkedTo instanceof Highcharts.Series) ? 'series' : null; | ||
| 176 | |||
| 177 | if (linkType === 'point') { | ||
| 178 | options.xValue = linkedTo.x; | ||
| 179 | options.yValue = linkedTo.y; | ||
| 180 | series = linkedTo.series; | ||
| 181 | } else if (linkType === 'series') { | ||
| 182 | series = linkedTo; | ||
| 183 | } | ||
| 184 | |||
| 185 | if (group.visibility !== series.group.visibility) { | ||
| 186 | group.attr({ | ||
| 187 | visibility: series.group.visibility | ||
| 188 | }); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | |||
| 193 | // Based on given options find annotation pixel position | ||
| 194 | x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x); | ||
| 195 | y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; | ||
| 196 | |||
| 197 | if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { | ||
| 198 | return; | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 202 | if (title) { | ||
| 203 | title.attr(options.title); | ||
| 204 | title.css(options.title.style); | ||
| 205 | resetBBox = true; | ||
| 206 | } | ||
| 207 | |||
| 208 | if (shape) { | ||
| 209 | shapeParams = extend({}, options.shape.params); | ||
| 210 | |||
| 211 | if (options.units === 'values') { | ||
| 212 | for (param in shapeParams) { | ||
| 213 | if (inArray(param, ['width', 'x']) > -1) { | ||
| 214 | shapeParams[param] = xAxis.translate(shapeParams[param]); | ||
| 215 | } else if (inArray(param, ['height', 'y']) > -1) { | ||
| 216 | shapeParams[param] = yAxis.translate(shapeParams[param]); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | if (shapeParams.width) { | ||
| 221 | shapeParams.width -= xAxis.toPixels(0) - xAxis.left; | ||
| 222 | } | ||
| 223 | |||
| 224 | if (shapeParams.x) { | ||
| 225 | shapeParams.x += xAxis.minPixelPadding; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (options.shape.type === 'path') { | ||
| 229 | translatePath(shapeParams.d, xAxis, yAxis, x, y); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | // move the center of the circle to shape x/y | ||
| 234 | if (options.shape.type === 'circle') { | ||
| 235 | shapeParams.x += shapeParams.r; | ||
| 236 | shapeParams.y += shapeParams.r; | ||
| 237 | } | ||
| 238 | |||
| 239 | resetBBox = true; | ||
| 240 | shape.attr(shapeParams); | ||
| 241 | } | ||
| 242 | |||
| 243 | group.bBox = null; | ||
| 244 | |||
| 245 | // If annotation width or height is not defined in options use bounding box size | ||
| 246 | if (!isNumber(width)) { | ||
| 247 | bbox = group.getBBox(); | ||
| 248 | width = bbox.width; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (!isNumber(height)) { | ||
| 252 | // get bbox only if it wasn't set before | ||
| 253 | if (!bbox) { | ||
| 254 | bbox = group.getBBox(); | ||
| 255 | } | ||
| 256 | |||
| 257 | height = bbox.height; | ||
| 258 | } | ||
| 259 | |||
| 260 | // Calculate anchor point | ||
| 261 | if (!isNumber(anchorX)) { | ||
| 262 | anchorX = ALIGN_FACTOR.center; | ||
| 263 | } | ||
| 264 | |||
| 265 | if (!isNumber(anchorY)) { | ||
| 266 | anchorY = ALIGN_FACTOR.center; | ||
| 267 | } | ||
| 268 | |||
| 269 | // Translate group according to its dimension and anchor point | ||
| 270 | x = x - width * anchorX; | ||
| 271 | y = y - height * anchorY; | ||
| 272 | |||
| 273 | if (chart.animation && defined(group.translateX) && defined(group.translateY)) { | ||
| 274 | group.animate({ | ||
| 275 | translateX: x, | ||
| 276 | translateY: y | ||
| 277 | }); | ||
| 278 | } else { | ||
| 279 | group.translate(x, y); | ||
| 280 | } | ||
| 281 | }, | ||
| 282 | |||
| 283 | /* | ||
| 284 | * Destroy the annotation | ||
| 285 | */ | ||
| 286 | destroy: function () { | ||
| 287 | var annotation = this, | ||
| 288 | chart = this.chart, | ||
| 289 | allItems = chart.annotations.allItems, | ||
| 290 | index = allItems.indexOf(annotation); | ||
| 291 | |||
| 292 | if (index > -1) { | ||
| 293 | allItems.splice(index, 1); | ||
| 294 | } | ||
| 295 | |||
| 296 | each(['title', 'shape', 'group'], function (element) { | ||
| 297 | if (annotation[element]) { | ||
| 298 | annotation[element].destroy(); | ||
| 299 | annotation[element] = null; | ||
| 300 | } | ||
| 301 | }); | ||
| 302 | |||
| 303 | annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; | ||
| 304 | }, | ||
| 305 | |||
| 306 | /* | ||
| 307 | * Update the annotation with a given options | ||
| 308 | */ | ||
| 309 | update: function (options, redraw) { | ||
| 310 | extend(this.options, options); | ||
| 311 | |||
| 312 | // update link to point or series | ||
| 313 | this.linkObjects(); | ||
| 314 | |||
| 315 | this.render(redraw); | ||
| 316 | }, | ||
| 317 | |||
| 318 | linkObjects: function () { | ||
| 319 | var annotation = this, | ||
| 320 | chart = annotation.chart, | ||
| 321 | linkedTo = annotation.linkedObject, | ||
| 322 | linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), | ||
| 323 | options = annotation.options, | ||
| 324 | id = options.linkedTo; | ||
| 325 | |||
| 326 | if (!defined(id)) { | ||
| 327 | annotation.linkedObject = null; | ||
| 328 | } else if (!defined(linkedTo) || id !== linkedId) { | ||
| 329 | annotation.linkedObject = chart.get(id); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | }; | ||
| 333 | |||
| 334 | |||
| 335 | // Add annotations methods to chart prototype | ||
| 336 | extend(Chart.prototype, { | ||
| 337 | annotations: { | ||
| 338 | /* | ||
| 339 | * Unified method for adding annotations to the chart | ||
| 340 | */ | ||
| 341 | add: function (options, redraw) { | ||
| 342 | var annotations = this.allItems, | ||
| 343 | chart = this.chart, | ||
| 344 | item, | ||
| 345 | len; | ||
| 346 | |||
| 347 | if (!isArray(options)) { | ||
| 348 | options = [options]; | ||
| 349 | } | ||
| 350 | |||
| 351 | len = options.length; | ||
| 352 | |||
| 353 | while (len--) { | ||
| 354 | item = new Annotation(chart, options[len]); | ||
| 355 | annotations.push(item); | ||
| 356 | item.render(redraw); | ||
| 357 | } | ||
| 358 | }, | ||
| 359 | |||
| 360 | /** | ||
| 361 | * Redraw all annotations, method used in chart events | ||
| 362 | */ | ||
| 363 | redraw: function () { | ||
| 364 | each(this.allItems, function (annotation) { | ||
| 365 | annotation.redraw(); | ||
| 366 | }); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | }); | ||
| 370 | |||
| 371 | |||
| 372 | // Initialize on chart load | ||
| 373 | Chart.prototype.callbacks.push(function (chart) { | ||
| 374 | var options = chart.options.annotations, | ||
| 375 | group; | ||
| 376 | |||
| 377 | group = chart.renderer.g("annotations"); | ||
| 378 | group.attr({ | ||
| 379 | zIndex: 7 | ||
| 380 | }); | ||
| 381 | group.add(); | ||
| 382 | |||
| 383 | // initialize empty array for annotations | ||
| 384 | chart.annotations.allItems = []; | ||
| 385 | |||
| 386 | // link chart object to annotations | ||
| 387 | chart.annotations.chart = chart; | ||
| 388 | |||
| 389 | // link annotations group element to the chart | ||
| 390 | chart.annotations.group = group; | ||
| 391 | |||
| 392 | if (isArray(options) && options.length > 0) { | ||
| 393 | chart.annotations.add(chart.options.annotations); | ||
| 394 | } | ||
| 395 | |||
| 396 | // update annotations after chart redraw | ||
| 397 | Highcharts.addEvent(chart, 'redraw', function () { | ||
| 398 | chart.annotations.redraw(); | ||
| 399 | }); | ||
| 400 | }); | ||
| 401 | }(Highcharts, HighchartsAdapter)); |
| 1 | /* | ||
| 2 | Data plugin for Highcharts | ||
| 3 | |||
| 4 | (c) 2012-2013 Torstein Hønsi | ||
| 5 | Last revision 2013-06-07 | ||
| 6 | |||
| 7 | License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | (function(h){var k=h.each,m=function(b,a){this.init(b,a)};h.extend(m.prototype,{init:function(b,a){this.options=b;this.chartOptions=a;this.columns=b.columns||this.rowsToColumns(b.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},getColumnDistribution:function(){var b=this.chartOptions,a=b&&b.chart&&b.chart.type,c=[];k(b&&b.series||[],function(b){c.push((h.seriesTypes[b.type||a||"line"].prototype.pointArrayMap||[0]).length)});this.valueCount= | ||
| 10 | {global:(h.seriesTypes[a||"line"].prototype.pointArrayMap||[0]).length,individual:c}},dataFound:function(){this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var b=this,a=this.options,c=a.csv,d=this.columns,f=a.startRow||0,i=a.endRow||Number.MAX_VALUE,j=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g=0;c&&(c=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(a.lineDelimiter||"\n"),k(c,function(c,h){var n=b.trim(c),p=n.indexOf("#")===0;h>=f&&h<=i&&!p&&n!==""&& | ||
| 11 | (n=c.split(a.itemDelimiter||","),k(n,function(b,a){a>=j&&a<=e&&(d[a-j]||(d[a-j]=[]),d[a-j][g]=b)}),g+=1)}),this.dataFound())},parseTable:function(){var b=this.options,a=b.table,c=this.columns,d=b.startRow||0,f=b.endRow||Number.MAX_VALUE,i=b.startColumn||0,j=b.endColumn||Number.MAX_VALUE,e;a&&(typeof a==="string"&&(a=document.getElementById(a)),k(a.getElementsByTagName("tr"),function(a,b){e=0;b>=d&&b<=f&&k(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&e>=i&&e<=j)c[e]||(c[e]=[]), | ||
| 12 | c[e][b-d]=a.innerHTML,e+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var b=this,a=this.options,c=a.googleSpreadsheetKey,d=this.columns,f=a.startRow||0,i=a.endRow||Number.MAX_VALUE,j=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g,h;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(a.googleSpreadsheetWorksheet||"od6")+"/public/values?alt=json-in-script&callback=?",function(a){var a=a.feed.entry,c,k=a.length,m=0,o=0,l;for(l=0;l<k;l++)c=a[l],m=Math.max(m,c.gs$cell.col), | ||
| 13 | o=Math.max(o,c.gs$cell.row);for(l=0;l<m;l++)if(l>=j&&l<=e)d[l-j]=[],d[l-j].length=Math.min(o,i-f);for(l=0;l<k;l++)if(c=a[l],g=c.gs$cell.row-1,h=c.gs$cell.col-1,h>=j&&h<=e&&g>=f&&g<=i)d[h-j][g-f]=c.content.$t;b.dataFound()})},findHeaderRow:function(){k(this.columns,function(){});this.headerRow=0},trim:function(b){return typeof b==="string"?b.replace(/^\s+|\s+$/g,""):b},parseTypes:function(){for(var b=this.columns,a=b.length,c,d,f,i;a--;)for(c=b[a].length;c--;)d=b[a][c],f=parseFloat(d),i=this.trim(d), | ||
| 14 | i==f?(b[a][c]=f,f>31536E6?b[a].isDatetime=!0:b[a].isNumeric=!0):(d=this.parseDate(d),a===0&&typeof d==="number"&&!isNaN(d)?(b[a][c]=d,b[a].isDatetime=!0):b[a][c]=i===""?null:i)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(b){return Date.UTC(+b[1],b[2]-1,+b[3])}}},parseDate:function(b){var a=this.options.parseDate,c,d,f;a&&(c=a(b));if(typeof b==="string")for(d in this.dateFormats)a=this.dateFormats[d],(f=b.match(a.regex))&&(c=a.parser(f));return c},rowsToColumns:function(b){var a, | ||
| 15 | c,d,f,i;if(b){i=[];c=b.length;for(a=0;a<c;a++){f=b[a].length;for(d=0;d<f;d++)i[d]||(i[d]=[]),i[d][a]=b[a][d]}}return i},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var b=this.columns,a,c,d=this.options,f,i,j,e,g,k;if(d.complete){this.getColumnDistribution();b.length>1&&(a=b.shift(),this.headerRow===0&&a.shift(),a.isDatetime?c="datetime":a.isNumeric||(c="category"));for(e=0;e<b.length;e++)if(this.headerRow===0)b[e].name=b[e].shift();i=[]; | ||
| 16 | for(e=0,k=0;e<b.length;k++){f=h.pick(this.valueCount.individual[k],this.valueCount.global);j=[];for(g=0;g<b[e].length;g++)j[g]=[a[g],b[e][g]!==void 0?b[e][g]:null],f>1&&j[g].push(b[e+1][g]!==void 0?b[e+1][g]:null),f>2&&j[g].push(b[e+2][g]!==void 0?b[e+2][g]:null),f>3&&j[g].push(b[e+3][g]!==void 0?b[e+3][g]:null),f>4&&j[g].push(b[e+4][g]!==void 0?b[e+4][g]:null);i[k]={name:b[e].name,data:j};e+=f}d.complete({xAxis:{type:c},series:i})}}});h.Data=m;h.data=function(b,a){return new m(b,a)};h.wrap(h.Chart.prototype, | ||
| 17 | "init",function(b,a,c){var d=this;a&&a.data?h.data(h.extend(a.data,{complete:function(f){a.series&&k(a.series,function(b,c){a.series[c]=h.merge(b,f.series[c])});a=h.merge(f,a);b.call(d,a,c)}}),a):b.call(d,a,c)})})(Highcharts); |
| 1 | (function(e){function q(b,a,c){return"rgba("+[Math.round(b[0]+(a[0]-b[0])*c),Math.round(b[1]+(a[1]-b[1])*c),Math.round(b[2]+(a[2]-b[2])*c),b[3]+(a[3]-b[3])*c].join(",")+")"}var m=function(){},j=e.getOptions(),g=e.each,n=e.extend,o=e.wrap,h=e.Chart,i=e.seriesTypes,k=i.pie,l=i.column,r=HighchartsAdapter.fireEvent;n(j.lang,{drillUpText:"◁ Back to {series.name}"});j.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#039",fontWeight:"bold",textDecoration:"underline"},activeDataLabelStyle:{cursor:"pointer", | ||
| 2 | color:"#039",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};e.SVGRenderer.prototype.Element.prototype.fadeIn=function(){this.attr({opacity:0.1,visibility:"visible"}).animate({opacity:1},{duration:250})};h.prototype.drilldownLevels=[];h.prototype.addSeriesAsDrilldown=function(b,a){var c=b.series,d=c.xAxis,f=c.yAxis,e;e=b.color||c.color;var g,a=n({color:e},a);g=HighchartsAdapter.inArray(this,c.points);this.drilldownLevels.push({seriesOptions:c.userOptions, | ||
| 3 | shapeArgs:b.shapeArgs,bBox:b.graphic.getBBox(),color:e,newSeries:a,pointOptions:c.options.data[g],pointIndex:g,oldExtremes:{xMin:d&&d.userMin,xMax:d&&d.userMax,yMin:f&&f.userMin,yMax:f&&f.userMax}});e=this.addSeries(a,!1);if(d)d.oldPos=d.pos,d.userMin=d.userMax=null,f.userMin=f.userMax=null;if(c.type===e.type)e.animate=e.animateDrilldown||m,e.options.animation=!0;c.remove(!1);this.redraw();this.showDrillUpButton()};h.prototype.getDrilldownBackText=function(){return this.options.lang.drillUpText.replace("{series.name}", | ||
| 4 | this.drilldownLevels[this.drilldownLevels.length-1].seriesOptions.name)};h.prototype.showDrillUpButton=function(){var b=this,a=this.getDrilldownBackText(),c=b.options.drilldown.drillUpButton;this.drillUpButton?this.drillUpButton.attr({text:a}).align():this.drillUpButton=this.renderer.button(a,null,null,function(){b.drillUp()}).attr(n({align:c.position.align,zIndex:9},c.theme)).add().align(c.position,!1,c.relativeTo||"plotBox")};h.prototype.drillUp=function(){var b=this.drilldownLevels.pop(),a=this.series[0], | ||
| 5 | c=b.oldExtremes,d=this.addSeries(b.seriesOptions,!1);r(this,"drillup",{seriesOptions:b.seriesOptions});if(d.type===a.type)d.drilldownLevel=b,d.animate=d.animateDrillupTo||m,d.options.animation=!0,a.animateDrillupFrom&&a.animateDrillupFrom(b);a.remove(!1);d.xAxis&&(d.xAxis.setExtremes(c.xMin,c.xMax,!1),d.yAxis.setExtremes(c.yMin,c.yMax,!1));this.redraw();this.drilldownLevels.length===0?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align()}; | ||
| 6 | k.prototype.animateDrilldown=function(b){var a=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],c=this.chart.options.drilldown.animation,d=a.shapeArgs,f=d.start,s=(d.end-f)/this.points.length,h=e.Color(a.color).rgba;b||g(this.points,function(a,b){var g=e.Color(a.color).rgba;a.graphic.attr(e.merge(d,{start:f+b*s,end:f+(b+1)*s})).animate(a.shapeArgs,e.merge(c,{step:function(a,d){d.prop==="start"&&this.attr({fill:q(h,g,d.pos)})}}))})};k.prototype.animateDrillupTo=l.prototype.animateDrillupTo= | ||
| 7 | function(b){if(!b){var a=this,c=a.drilldownLevel;g(this.points,function(a){a.graphic.hide();a.dataLabel&&a.dataLabel.hide();a.connector&&a.connector.hide()});setTimeout(function(){g(a.points,function(a,b){var e=b===c.pointIndex?"show":"fadeIn";a.graphic[e]();if(a.dataLabel)a.dataLabel[e]();if(a.connector)a.connector[e]()})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=m}};l.prototype.animateDrilldown=function(b){var a=this.chart.drilldownLevels[this.chart.drilldownLevels.length- | ||
| 8 | 1].shapeArgs,c=this.chart.options.drilldown.animation;b||(a.x+=this.xAxis.oldPos-this.xAxis.pos,g(this.points,function(b){b.graphic.attr(a).animate(b.shapeArgs,c)}))};l.prototype.animateDrillupFrom=k.prototype.animateDrillupFrom=function(b){var a=this.chart.options.drilldown.animation,c=this.group;delete this.group;g(this.points,function(d){var f=d.graphic,g=e.Color(d.color).rgba;delete d.graphic;f.animate(b.shapeArgs,e.merge(a,{step:function(a,c){c.prop==="start"&&this.attr({fill:q(g,e.Color(b.color).rgba, | ||
| 9 | c.pos)})},complete:function(){f.destroy();c&&(c=c.destroy())}}))})};e.Point.prototype.doDrilldown=function(){for(var b=this.series.chart,a=b.options.drilldown,c=a.series.length,d;c--&&!d;)a.series[c].id===this.drilldown&&(d=a.series[c]);r(b,"drilldown",{point:this,seriesOptions:d});d&&b.addSeriesAsDrilldown(this,d)};o(e.Point.prototype,"init",function(b,a,c,d){var f=b.call(this,a,c,d),b=a.chart,a=(a=a.xAxis&&a.xAxis.ticks[d])&&a.label;if(f.drilldown){if(e.addEvent(f,"click",function(){f.doDrilldown()}), | ||
| 10 | a){if(!a._basicStyle)a._basicStyle=a.element.getAttribute("style");a.addClass("highcharts-drilldown-axis-label").css(b.options.drilldown.activeAxisLabelStyle).on("click",function(){f.doDrilldown&&f.doDrilldown()})}}else a&&a._basicStyle&&a.element.setAttribute("style",a._basicStyle);return f});o(e.Series.prototype,"drawDataLabels",function(b){var a=this.chart.options.drilldown.activeDataLabelStyle;b.call(this);g(this.points,function(b){if(b.drilldown&&b.dataLabel)b.dataLabel.attr({"class":"highcharts-drilldown-data-label"}).css(a).on("click", | ||
| 11 | function(){b.doDrilldown()})})});l.prototype.supportsDrilldown=!0;k.prototype.supportsDrilldown=!0;var p,j=function(b){b.call(this);g(this.points,function(a){a.drilldown&&a.graphic&&a.graphic.attr({"class":"highcharts-drilldown-point"}).css({cursor:"pointer"})})};for(p in i)i[p].prototype.supportsDrilldown&&o(i[p].prototype,"drawTracker",j)})(Highcharts); |
| 1 | /* | ||
| 2 | Highcharts JS v3.0.6 (2013-10-04) | ||
| 3 | Exporting module | ||
| 4 | |||
| 5 | (c) 2010-2013 Torstein Hønsi | ||
| 6 | |||
| 7 | License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | (function(f){var A=f.Chart,t=f.addEvent,C=f.removeEvent,k=f.createElement,n=f.discardElement,u=f.css,o=f.merge,r=f.each,p=f.extend,D=Math.max,j=document,B=window,E=f.isTouchDevice,F=f.Renderer.prototype.symbols,x=f.getOptions(),y;p(x.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});x.navigation={menuStyle:{border:"1px solid #A0A0A0", | ||
| 10 | background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:E?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};x.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{menuClassName:"highcharts-contextmenu", | ||
| 11 | symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(c,a){var d,b;b=k("form",{method:"post", | ||
| 12 | action:c,enctype:"multipart/form-data"},{display:"none"},j.body);for(d in a)k("input",{type:"hidden",name:d,value:a[d]},null,b);b.submit();n(b)};p(A.prototype,{getSVG:function(c){var a=this,d,b,z,h,g=o(a.options,c);if(!j.createElementNS)j.createElementNS=function(a,b){return j.createElement(b)};c=k("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},j.body);b=a.renderTo.style.width;h=a.renderTo.style.height;b=g.exporting.sourceWidth||g.chart.width||/px$/.test(b)&& | ||
| 13 | parseInt(b,10)||600;h=g.exporting.sourceHeight||g.chart.height||/px$/.test(h)&&parseInt(h,10)||400;p(g.chart,{animation:!1,renderTo:c,forExport:!0,width:b,height:h});g.exporting.enabled=!1;g.series=[];r(a.series,function(a){z=o(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});z.isInternal||g.series.push(z)});d=new f.Chart(g,a.callback);r(["xAxis","yAxis"],function(b){r(a[b],function(a,c){var g=d[b][c],f=a.getExtremes(),h=f.userMin,f=f.userMax;g&&(h!==void 0||f!==void 0)&&g.setExtremes(h, | ||
| 14 | f,!0,!1)})});b=d.container.innerHTML;g=null;d.destroy();n(c);b=b.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/ /g," ").replace(/­/g,"").replace(/<IMG /g,"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g, | ||
| 15 | 'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});return b=b.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},exportChart:function(c,a){var c=c||{},d=this.options.exporting,d=this.getSVG(o({chart:{borderRadius:0}},d.chartOptions,a,{exporting:{sourceWidth:c.sourceWidth|| | ||
| 16 | d.sourceWidth,sourceHeight:c.sourceHeight||d.sourceHeight}})),c=o(this.options.exporting,c);f.post(c.url,{filename:c.filename||"chart",type:c.type,width:c.width||0,scale:c.scale||2,svg:d})},print:function(){var c=this,a=c.container,d=[],b=a.parentNode,f=j.body,h=f.childNodes;if(!c.isPrinting)c.isPrinting=!0,r(h,function(a,b){if(a.nodeType===1)d[b]=a.style.display,a.style.display="none"}),f.appendChild(a),B.focus(),B.print(),setTimeout(function(){b.appendChild(a);r(h,function(a,b){if(a.nodeType=== | ||
| 17 | 1)a.style.display=d[b]});c.isPrinting=!1},1E3)},contextMenu:function(c,a,d,b,f,h,g){var e=this,j=e.options.navigation,q=j.menuItemStyle,l=e.chartWidth,m=e.chartHeight,o="cache-"+c,i=e[o],s=D(f,h),v,w,n;if(!i)e[o]=i=k("div",{className:c},{position:"absolute",zIndex:1E3,padding:s+"px"},e.container),v=k("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},j.menuStyle),i),w=function(){u(i,{display:"none"});g&&g.setState(0);e.openMenu=!1},t(i, | ||
| 18 | "mouseleave",function(){n=setTimeout(w,500)}),t(i,"mouseenter",function(){clearTimeout(n)}),t(document,"mousedown",function(a){e.pointer.inClass(a.target,c)||w()}),r(a,function(a){if(a){var b=a.separator?k("hr",null,null,v):k("div",{onmouseover:function(){u(this,j.menuItemHoverStyle)},onmouseout:function(){u(this,q)},onclick:function(){w();a.onclick.apply(e,arguments)},innerHTML:a.text||e.options.lang[a.textKey]},p({cursor:"pointer"},q),v);e.exportDivElements.push(b)}}),e.exportDivElements.push(v, | ||
| 19 | i),e.exportMenuWidth=i.offsetWidth,e.exportMenuHeight=i.offsetHeight;a={display:"block"};d+e.exportMenuWidth>l?a.right=l-d-f-s+"px":a.left=d-s+"px";b+h+e.exportMenuHeight>m&&g.alignOptions.verticalAlign!=="top"?a.bottom=m-b-s+"px":a.top=b+h-s+"px";u(i,a);e.openMenu=!0},addButton:function(c){var a=this,d=a.renderer,b=o(a.options.navigation.buttonOptions,c),j=b.onclick,h=b.menuItems,g,e,k={stroke:b.symbolStroke,fill:b.symbolFill},q=b.symbolSize||12;if(!a.btnCount)a.btnCount=0;if(!a.exportDivElements)a.exportDivElements= | ||
| 20 | [],a.exportSVGElements=[];if(b.enabled!==!1){var l=b.theme,m=l.states,n=m&&m.hover,m=m&&m.select,i;delete l.states;j?i=function(){j.apply(a,arguments)}:h&&(i=function(){a.contextMenu(e.menuClassName,h,e.translateX,e.translateY,e.width,e.height,e);e.setState(2)});b.text&&b.symbol?l.paddingLeft=f.pick(l.paddingLeft,25):b.text||p(l,{width:b.width,height:b.height,padding:0});e=d.button(b.text,0,0,i,l,n,m).attr({title:a.options.lang[b._titleKey],"stroke-linecap":"round"});e.menuClassName=c.menuClassName|| | ||
| 21 | "highcharts-menu-"+a.btnCount++;b.symbol&&(g=d.symbol(b.symbol,b.symbolX-q/2,b.symbolY-q/2,q,q).attr(p(k,{"stroke-width":b.symbolStrokeWidth||1,zIndex:1})).add(e));e.add().align(p(b,{width:e.width,x:f.pick(b.x,y)}),!0,"spacingBox");y+=(e.width+b.buttonSpacing)*(b.align==="right"?-1:1);a.exportSVGElements.push(e,g)}},destroyExport:function(c){var c=c.target,a,d;for(a=0;a<c.exportSVGElements.length;a++)if(d=c.exportSVGElements[a])d.onclick=d.ontouchstart=null,c.exportSVGElements[a]=d.destroy();for(a= | ||
| 22 | 0;a<c.exportDivElements.length;a++)d=c.exportDivElements[a],C(d,"mouseleave"),c.exportDivElements[a]=d.onmouseout=d.onmouseover=d.ontouchstart=d.onclick=null,n(d)}});F.menu=function(c,a,d,b){return["M",c,a+2.5,"L",c+d,a+2.5,"M",c,a+b/2+0.5,"L",c+d,a+b/2+0.5,"M",c,a+b-1.5,"L",c+d,a+b-1.5]};A.prototype.callbacks.push(function(c){var a,d=c.options.exporting,b=d.buttons;y=0;if(d.enabled!==!1){for(a in b)c.addButton(b[a]);t(c,"destroy",c.destroyExport)}})})(Highcharts); |
| 1 | /* | ||
| 2 | |||
| 3 | Highcharts funnel module, Beta | ||
| 4 | |||
| 5 | (c) 2010-2012 Torstein Hønsi | ||
| 6 | |||
| 7 | License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | (function(d){var u=d.getOptions().plotOptions,p=d.seriesTypes,D=d.merge,z=function(){},A=d.each;u.funnel=D(u.pie,{center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",dataLabels:{connectorWidth:1,connectorColor:"#606060"},size:!0,states:{select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}}});p.funnel=d.extendClass(p.pie,{type:"funnel",animate:z,translate:function(){var a=function(k,a){return/%$/.test(k)?a*parseInt(k,10)/100:parseInt(k,10)},g=0,e=this.chart,f=e.plotWidth, | ||
| 10 | e=e.plotHeight,h=0,c=this.options,C=c.center,b=a(C[0],f),d=a(C[0],e),p=a(c.width,f),i,q,j=a(c.height,e),r=a(c.neckWidth,f),s=a(c.neckHeight,e),v=j-s,a=this.data,w,x,u=c.dataLabels.position==="left"?1:0,y,m,B,n,l,t,o;this.getWidthAt=q=function(k){return k>j-s||j===s?r:r+(p-r)*((j-s-k)/(j-s))};this.getX=function(k,a){return b+(a?-1:1)*(q(k)/2+c.dataLabels.distance)};this.center=[b,d,j];this.centerX=b;A(a,function(a){g+=a.y});A(a,function(a){o=null;x=g?a.y/g:0;m=d-j/2+h*j;l=m+x*j;i=q(m);y=b-i/2;B=y+ | ||
| 11 | i;i=q(l);n=b-i/2;t=n+i;m>v?(y=n=b-r/2,B=t=b+r/2):l>v&&(o=l,i=q(v),n=b-i/2,t=n+i,l=v);w=["M",y,m,"L",B,m,t,l];o&&w.push(t,o,n,o);w.push(n,l,"Z");a.shapeType="path";a.shapeArgs={d:w};a.percentage=x*100;a.plotX=b;a.plotY=(m+(o||l))/2;a.tooltipPos=[b,a.plotY];a.slice=z;a.half=u;h+=x});this.setTooltipPoints()},drawPoints:function(){var a=this,g=a.options,e=a.chart.renderer;A(a.data,function(f){var h=f.graphic,c=f.shapeArgs;h?h.animate(c):f.graphic=e.path(c).attr({fill:f.color,stroke:g.borderColor,"stroke-width":g.borderWidth}).add(a.group)})}, | ||
| 12 | sortByAngle:z,drawDataLabels:function(){var a=this.data,g=this.options.dataLabels.distance,e,f,h,c=a.length,d,b;for(this.center[2]-=2*g;c--;)h=a[c],f=(e=h.half)?1:-1,b=h.plotY,d=this.getX(b,e),h.labelPos=[0,b,d+(g-5)*f,b,d+g*f,b,e?"right":"left",0];p.pie.prototype.drawDataLabels.call(this)}})})(Highcharts); |
| 1 | /** | ||
| 2 | * @license | ||
| 3 | * Highcharts funnel module, Beta | ||
| 4 | * | ||
| 5 | * (c) 2010-2012 Torstein Hønsi | ||
| 6 | * | ||
| 7 | * License: www.highcharts.com/license | ||
| 8 | */ | ||
| 9 | |||
| 10 | /*global Highcharts */ | ||
| 11 | (function (Highcharts) { | ||
| 12 | |||
| 13 | 'use strict'; | ||
| 14 | |||
| 15 | // create shortcuts | ||
| 16 | var defaultOptions = Highcharts.getOptions(), | ||
| 17 | defaultPlotOptions = defaultOptions.plotOptions, | ||
| 18 | seriesTypes = Highcharts.seriesTypes, | ||
| 19 | merge = Highcharts.merge, | ||
| 20 | noop = function () {}, | ||
| 21 | each = Highcharts.each; | ||
| 22 | |||
| 23 | // set default options | ||
| 24 | defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, { | ||
| 25 | center: ['50%', '50%'], | ||
| 26 | width: '90%', | ||
| 27 | neckWidth: '30%', | ||
| 28 | height: '100%', | ||
| 29 | neckHeight: '25%', | ||
| 30 | |||
| 31 | dataLabels: { | ||
| 32 | //position: 'right', | ||
| 33 | connectorWidth: 1, | ||
| 34 | connectorColor: '#606060' | ||
| 35 | }, | ||
| 36 | size: true, // to avoid adapting to data label size in Pie.drawDataLabels | ||
| 37 | states: { | ||
| 38 | select: { | ||
| 39 | color: '#C0C0C0', | ||
| 40 | borderColor: '#000000', | ||
| 41 | shadow: false | ||
| 42 | } | ||
| 43 | } | ||
| 44 | }); | ||
| 45 | |||
| 46 | |||
| 47 | seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, { | ||
| 48 | |||
| 49 | type: 'funnel', | ||
| 50 | animate: noop, | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Overrides the pie translate method | ||
| 54 | */ | ||
| 55 | translate: function () { | ||
| 56 | |||
| 57 | var | ||
| 58 | // Get positions - either an integer or a percentage string must be given | ||
| 59 | getLength = function (length, relativeTo) { | ||
| 60 | return (/%$/).test(length) ? | ||
| 61 | relativeTo * parseInt(length, 10) / 100 : | ||
| 62 | parseInt(length, 10); | ||
| 63 | }, | ||
| 64 | |||
| 65 | sum = 0, | ||
| 66 | series = this, | ||
| 67 | chart = series.chart, | ||
| 68 | plotWidth = chart.plotWidth, | ||
| 69 | plotHeight = chart.plotHeight, | ||
| 70 | cumulative = 0, // start at top | ||
| 71 | options = series.options, | ||
| 72 | center = options.center, | ||
| 73 | centerX = getLength(center[0], plotWidth), | ||
| 74 | centerY = getLength(center[0], plotHeight), | ||
| 75 | width = getLength(options.width, plotWidth), | ||
| 76 | tempWidth, | ||
| 77 | getWidthAt, | ||
| 78 | height = getLength(options.height, plotHeight), | ||
| 79 | neckWidth = getLength(options.neckWidth, plotWidth), | ||
| 80 | neckHeight = getLength(options.neckHeight, plotHeight), | ||
| 81 | neckY = height - neckHeight, | ||
| 82 | data = series.data, | ||
| 83 | path, | ||
| 84 | fraction, | ||
| 85 | half = options.dataLabels.position === 'left' ? 1 : 0, | ||
| 86 | |||
| 87 | x1, | ||
| 88 | y1, | ||
| 89 | x2, | ||
| 90 | x3, | ||
| 91 | y3, | ||
| 92 | x4, | ||
| 93 | y5; | ||
| 94 | |||
| 95 | // Return the width at a specific y coordinate | ||
| 96 | series.getWidthAt = getWidthAt = function (y) { | ||
| 97 | return y > height - neckHeight || height === neckHeight ? | ||
| 98 | neckWidth : | ||
| 99 | neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight)); | ||
| 100 | }; | ||
| 101 | series.getX = function (y, half) { | ||
| 102 | return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance); | ||
| 103 | }; | ||
| 104 | |||
| 105 | // Expose | ||
| 106 | series.center = [centerX, centerY, height]; | ||
| 107 | series.centerX = centerX; | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Individual point coordinate naming: | ||
| 111 | * | ||
| 112 | * x1,y1 _________________ x2,y1 | ||
| 113 | * \ / | ||
| 114 | * \ / | ||
| 115 | * \ / | ||
| 116 | * \ / | ||
| 117 | * \ / | ||
| 118 | * x3,y3 _________ x4,y3 | ||
| 119 | * | ||
| 120 | * Additional for the base of the neck: | ||
| 121 | * | ||
| 122 | * | | | ||
| 123 | * | | | ||
| 124 | * | | | ||
| 125 | * x3,y5 _________ x4,y5 | ||
| 126 | */ | ||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | |||
| 131 | // get the total sum | ||
| 132 | each(data, function (point) { | ||
| 133 | sum += point.y; | ||
| 134 | }); | ||
| 135 | |||
| 136 | each(data, function (point) { | ||
| 137 | // set start and end positions | ||
| 138 | y5 = null; | ||
| 139 | fraction = sum ? point.y / sum : 0; | ||
| 140 | y1 = centerY - height / 2 + cumulative * height; | ||
| 141 | y3 = y1 + fraction * height; | ||
| 142 | //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight)); | ||
| 143 | tempWidth = getWidthAt(y1); | ||
| 144 | x1 = centerX - tempWidth / 2; | ||
| 145 | x2 = x1 + tempWidth; | ||
| 146 | tempWidth = getWidthAt(y3); | ||
| 147 | x3 = centerX - tempWidth / 2; | ||
| 148 | x4 = x3 + tempWidth; | ||
| 149 | |||
| 150 | // the entire point is within the neck | ||
| 151 | if (y1 > neckY) { | ||
| 152 | x1 = x3 = centerX - neckWidth / 2; | ||
| 153 | x2 = x4 = centerX + neckWidth / 2; | ||
| 154 | |||
| 155 | // the base of the neck | ||
| 156 | } else if (y3 > neckY) { | ||
| 157 | y5 = y3; | ||
| 158 | |||
| 159 | tempWidth = getWidthAt(neckY); | ||
| 160 | x3 = centerX - tempWidth / 2; | ||
| 161 | x4 = x3 + tempWidth; | ||
| 162 | |||
| 163 | y3 = neckY; | ||
| 164 | } | ||
| 165 | |||
| 166 | // save the path | ||
| 167 | path = [ | ||
| 168 | 'M', | ||
| 169 | x1, y1, | ||
| 170 | 'L', | ||
| 171 | x2, y1, | ||
| 172 | x4, y3 | ||
| 173 | ]; | ||
| 174 | if (y5) { | ||
| 175 | path.push(x4, y5, x3, y5); | ||
| 176 | } | ||
| 177 | path.push(x3, y3, 'Z'); | ||
| 178 | |||
| 179 | // prepare for using shared dr | ||
| 180 | point.shapeType = 'path'; | ||
| 181 | point.shapeArgs = { d: path }; | ||
| 182 | |||
| 183 | |||
| 184 | // for tooltips and data labels | ||
| 185 | point.percentage = fraction * 100; | ||
| 186 | point.plotX = centerX; | ||
| 187 | point.plotY = (y1 + (y5 || y3)) / 2; | ||
| 188 | |||
| 189 | // Placement of tooltips and data labels | ||
| 190 | point.tooltipPos = [ | ||
| 191 | centerX, | ||
| 192 | point.plotY | ||
| 193 | ]; | ||
| 194 | |||
| 195 | // Slice is a noop on funnel points | ||
| 196 | point.slice = noop; | ||
| 197 | |||
| 198 | // Mimicking pie data label placement logic | ||
| 199 | point.half = half; | ||
| 200 | |||
| 201 | cumulative += fraction; | ||
| 202 | }); | ||
| 203 | |||
| 204 | |||
| 205 | series.setTooltipPoints(); | ||
| 206 | }, | ||
| 207 | /** | ||
| 208 | * Draw a single point (wedge) | ||
| 209 | * @param {Object} point The point object | ||
| 210 | * @param {Object} color The color of the point | ||
| 211 | * @param {Number} brightness The brightness relative to the color | ||
| 212 | */ | ||
| 213 | drawPoints: function () { | ||
| 214 | var series = this, | ||
| 215 | options = series.options, | ||
| 216 | chart = series.chart, | ||
| 217 | renderer = chart.renderer; | ||
| 218 | |||
| 219 | each(series.data, function (point) { | ||
| 220 | |||
| 221 | var graphic = point.graphic, | ||
| 222 | shapeArgs = point.shapeArgs; | ||
| 223 | |||
| 224 | if (!graphic) { // Create the shapes | ||
| 225 | point.graphic = renderer.path(shapeArgs). | ||
| 226 | attr({ | ||
| 227 | fill: point.color, | ||
| 228 | stroke: options.borderColor, | ||
| 229 | 'stroke-width': options.borderWidth | ||
| 230 | }). | ||
| 231 | add(series.group); | ||
| 232 | |||
| 233 | } else { // Update the shapes | ||
| 234 | graphic.animate(shapeArgs); | ||
| 235 | } | ||
| 236 | }); | ||
| 237 | }, | ||
| 238 | |||
| 239 | /** | ||
| 240 | * Funnel items don't have angles (#2289) | ||
| 241 | */ | ||
| 242 | sortByAngle: noop, | ||
| 243 | |||
| 244 | /** | ||
| 245 | * Extend the pie data label method | ||
| 246 | */ | ||
| 247 | drawDataLabels: function () { | ||
| 248 | var data = this.data, | ||
| 249 | labelDistance = this.options.dataLabels.distance, | ||
| 250 | leftSide, | ||
| 251 | sign, | ||
| 252 | point, | ||
| 253 | i = data.length, | ||
| 254 | x, | ||
| 255 | y; | ||
| 256 | |||
| 257 | // In the original pie label anticollision logic, the slots are distributed | ||
| 258 | // from one labelDistance above to one labelDistance below the pie. In funnels | ||
| 259 | // we don't want this. | ||
| 260 | this.center[2] -= 2 * labelDistance; | ||
| 261 | |||
| 262 | // Set the label position array for each point. | ||
| 263 | while (i--) { | ||
| 264 | point = data[i]; | ||
| 265 | leftSide = point.half; | ||
| 266 | sign = leftSide ? 1 : -1; | ||
| 267 | y = point.plotY; | ||
| 268 | x = this.getX(y, leftSide); | ||
| 269 | |||
| 270 | // set the anchor point for data labels | ||
| 271 | point.labelPos = [ | ||
| 272 | 0, // first break of connector | ||
| 273 | y, // a/a | ||
| 274 | x + (labelDistance - 5) * sign, // second break, right outside point shape | ||
| 275 | y, // a/a | ||
| 276 | x + labelDistance * sign, // landing point for connector | ||
| 277 | y, // a/a | ||
| 278 | leftSide ? 'right' : 'left', // alignment | ||
| 279 | 0 // center angle | ||
| 280 | ]; | ||
| 281 | } | ||
| 282 | |||
| 283 | seriesTypes.pie.prototype.drawDataLabels.call(this); | ||
| 284 | } | ||
| 285 | |||
| 286 | }); | ||
| 287 | |||
| 288 | |||
| 289 | }(Highcharts)); |
2.78 KB
2.62 KB
1.63 KB
1.24 KB
1.58 KB
1.7 KB
3.86 KB
35.7 KB
1.52 KB
1.86 KB
1.32 KB
26.7 KB
625 Bytes
830 Bytes
109 Bytes
441 Bytes
6.69 KB
12.3 KB
933 Bytes
381 Bytes
4.57 KB
5.16 KB
601 Bytes
580 Bytes
570 Bytes
762 Bytes
399 Bytes
710 Bytes
432 Bytes
534 Bytes
529 Bytes
467 Bytes
45 Bytes
381 Bytes
5.43 KB
10.9 KB
5.58 KB
52.8 KB
3.69 KB
366 Bytes
8.28 KB
20.8 KB
371 Bytes
1020 Bytes
11.4 KB
3.21 KB
1020 Bytes
11.4 KB
1.45 KB
1020 Bytes
11.4 KB
3.21 KB
1020 Bytes
11.4 KB
3.2 KB
1020 Bytes
11.4 KB
3.21 KB
1020 Bytes
11.4 KB
3.2 KB
137 Bytes
1020 Bytes
1.5 KB
11.4 KB
1.09 KB
1.11 KB
1.1 KB
1.22 KB
1.67 KB
1.28 KB
2.06 KB
1.63 KB
3.39 KB
1.69 KB
1.75 KB
1018 Bytes
1.1 KB
1.3 KB
4.02 KB
1.54 KB
3.74 KB
1.59 KB
2.14 KB
1.74 KB
2.39 KB
1.41 KB
2.12 KB
2.12 KB
7.84 KB
2.19 KB
1.7 KB
1.77 KB
1.53 KB
1.38 KB
7.95 KB
7.94 KB
1.76 KB
1.77 KB
1.82 KB
2.97 KB
1.9 KB
2.2 KB
2.39 KB
3.92 KB
2.59 KB
3.3 KB
4.79 KB
1.81 KB
1.74 KB
5.59 KB
7.2 KB
4.35 KB
2.09 KB
13.1 KB
1.38 KB
1.17 KB
1.63 KB
1.76 KB
1.93 KB
1.09 KB
2.49 KB
1.34 KB
1.23 KB
1.53 KB
4.37 KB
1015 Bytes
1.13 KB
991 Bytes
3.59 KB
4.56 KB
2.4 KB
2.6 KB
1.11 KB
971 Bytes
988 Bytes
1.13 KB
2.68 KB
1.12 KB
1012 Bytes
1.18 KB
3.15 KB
1.19 KB
1.48 KB
1.5 KB
1.55 KB
1.51 KB
654 Bytes
3.49 KB
1.14 KB
3.04 KB
10.2 KB
3.86 KB
3.29 KB
13.1 KB
4.22 KB
1.53 KB
4.58 KB
5.04 KB
3.73 KB
1.71 KB
6.21 KB
3 KB
4.88 KB
3.64 KB
1.5 KB
2.04 KB
2.33 KB
2.35 KB
4.88 KB
2.54 KB
1.5 KB
1.34 KB
2.14 KB
3.33 KB
1.55 KB
3.34 KB
1.54 KB
1.48 KB
1.52 KB
2.08 KB
1.19 KB
2.94 KB
1.75 KB
5.18 KB
1.12 KB
1.38 KB
1.11 KB
1.38 KB
1.12 KB
1.38 KB
1.12 KB
1.37 KB
1.09 KB
1.38 KB
1.11 KB
1.37 KB
1.12 KB
1.38 KB
1.11 KB
1.38 KB
1.1 KB
1.38 KB
1.11 KB
1.37 KB
221 Bytes
1.18 KB
3.9 KB
763 Bytes
2.27 KB
3.16 KB
3.18 KB
2.47 KB
2.08 KB
1.07 KB
1.09 KB
1.05 KB
723 Bytes
3.91 KB
1.54 KB
1.65 KB
1.88 KB
1.66 KB
4.32 KB
3.98 KB
3.93 KB
3.44 KB
3.67 KB
1.75 KB
990 Bytes
33.9 KB
27.3 KB
3.63 KB
3.7 KB
3.39 KB
3.64 KB
3.38 KB
37.7 KB
4.01 KB
3.13 KB
3.12 KB
4.47 KB
4.42 KB
27.3 KB
57.3 KB
70 KB
43.2 KB
55.8 KB
49.8 KB
37.7 KB
63.8 KB
40.8 KB
46.8 KB
70.3 KB
66.5 KB
2.32 KB
2.83 KB
1.91 KB
7.27 KB
7.63 KB
475 Bytes
48.1 KB
5.77 KB
5.66 KB
701 Bytes
1.75 KB
280 Bytes
8.28 KB
-
Please register or sign in to post a comment