8ea4cd8e by yangjianbin

init

1 parent 8c1e7eff
Showing 1000 changed files with 0 additions and 4903 deletions

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

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 public enum ActionState {
4 UNKNOW_ERROR
5 }
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 }
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 ![检查 .NET 4.0 安装情况](../_doc/images/net-publish-1.png)
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 ![转换为应用程序](../_doc/images/net-publish-2.png)
38
39 2. 弹出的对话框中,点击「选择...」来指定使用的应用程序池。选择版本为 4.0 的应用程序池,然后点确定。
40
41 ![选择应用程序池](../_doc/images/net-publish-3.png)
42
43 3. 设置连接凭据。点击「链接为...」按钮,在弹出的对话框中指定一个对目录具有读写权限的用户(如 administrator),然后点确定。
44
45 ![设置连接凭据](../_doc/images/net-publish-4.png)
46
47 设置完毕后,可以点击「测试设置...」来测试权限是否正常。
48
49 ![设置连接凭据](../_doc/images/net-publish-5.png)
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 /*可以在这里添加你自己的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(/&nbsp;/g," ").replace(/&shy;/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]+)&quot;/g,"$1").replace(/&quot;/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));
No preview for this file type
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!