├── composer.json
├── assets
└── FileUploadAsset.php
├── views
└── index.php
├── README.md
├── statics
├── js
│ ├── upload-input.js
│ ├── upload.js
│ └── jquery.form.js
└── css
│ └── upload.css
├── FileUpload.php
├── config.php
├── UploadAction.php
└── Uploader.php
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hyiifu/yii2-file-upload",
3 | "description": "The ueditor extension for the Yii framework",
4 | "keywords": ["yii2", "file-upload", "yii-china"],
5 | "homepage": "https://github.com/org-yii-china/yii2-file-upload.git",
6 | "type": "yii2-extension",
7 | "license": "BSD-3-Clause",
8 | "authors": [
9 | {
10 | "name": "Xianan Huang",
11 | "email": "xianan_huang@163.com"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=5.3.0"
16 | },
17 | "autoload": {
18 | "psr-4": {
19 | "yii-china\\file-upload\\": ""
20 | }
21 | },
22 | "extra": {
23 | "branch-alias": {
24 | "dev-master": "1.0.x-dev"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/assets/FileUploadAsset.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class FileUploadAsset extends AssetBundle
16 | {
17 | public $css = [
18 | 'css/upload.css',
19 |
20 | ];
21 |
22 | public $js = [
23 | 'js/jquery.form.js',
24 | 'js/upload.js',
25 | 'js/upload-input.js',
26 | ];
27 |
28 | public $depends = [
29 | 'yii\web\YiiAsset',
30 | ];
31 |
32 | /**
33 | * 初始化:sourcePath赋值
34 | * @see \yii\web\AssetBundle::init()
35 | */
36 | public function init()
37 | {
38 | $this->sourcePath = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR . 'statics';
39 | }
40 | }
--------------------------------------------------------------------------------
/views/index.php:
--------------------------------------------------------------------------------
1 |
5 | * 图片上传组件
6 | * 如何配置请到官网(Yii中文网)查看相关文章
7 | */
8 |
9 | use yii\helpers\Html;
10 | ?>
11 |
12 |
=isset($inputValue)?'

':''?>
13 |
图片上传
14 |
15 |
选择图片
16 |
仅支持文件格式为jpg、jpeg、png以及gif
大小在1MB以下的文件
17 |
18 |
' value="=isset($inputValue)?$inputValue:''?>" filetype="img" />
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Yii2 插件整合-图片上传(file-upload)
2 |
3 | 实例教程:http://www.yii-china.com/post/detail/15.html
4 |
5 | 安装扩展:
6 |
7 | 1.点击上面扩展下载下载扩展
8 |
9 | 然后重命名为file_upload放在/common/widgets文件夹中
10 |
11 | 2.在使用图片上传控件的控制器(controller)中,加入以下代码
12 |
13 | public function actions()
14 | {
15 | return [
16 | 'upload'=>[
17 | 'class' => 'common\widgets\file_upload\UploadAction', //这里扩展地址别写错
18 | 'config' => [
19 | 'imagePathFormat' => "/image/{yyyy}{mm}{dd}/{time}{rand:6}",
20 | ]
21 | ]
22 | ];
23 | }
24 |
25 | 3.views渲染图片上传界面有两种方式:
26 |
27 | 第一种:不带model
28 |
29 |
30 | use common\widgets\file_upload\FileUpload; //引入扩展
31 | echo FileUpload::widget();
32 | echo FileUpload::widget(['value'=>$url]); //如果编辑时要带默认图,$url为图片路径
33 |
34 |
35 | 第二种:带model
36 |
37 |
38 | $form = ActiveForm::begin();
39 | echo $form->field($model, 'label_img')->widget('common\widgets\file_upload\FileUpload',[
40 | 'config'=>[
41 | '图片上传的一些配置,不写调用默认配置'
42 | ]
43 | ]);
44 | ActiveForm::end();
45 |
46 |
47 |
--------------------------------------------------------------------------------
/statics/js/upload-input.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | var form_temp = [];
3 | var toAppend = [
4 | ''
7 | ].join('');
8 |
9 | form_temp.push(toAppend);
10 |
11 | if($('.form_temp').length < 1){
12 | $('body').append('');
13 | }
14 | $('.form_temp').append(form_temp.join(''));
15 | $('.choose_btn').click(function(){
16 | var input= $('#upfile');
17 | input.attr('from', $(this).attr('id'));
18 | input.click();
19 | });
20 | $('.file_upload').on('change',function(){
21 | var from=$('#upfile').attr('from');
22 | var thisUrl = $('.'+from).parent().attr('data-url');
23 | var thisType = $(this).attr('filetype'),
24 | maxSize = 1*1024*1024;
25 | $(this).upload({
26 | url : thisUrl,
27 | maxSize : maxSize,
28 | img : {
29 | img : ['jpg','JPG','jpeg','JPEG','gif','GIF','png','PNG']
30 | },
31 | sucFn : function(json){
32 | var from=$('#upfile').attr('from');
33 | var domainUrl = $('.'+from).attr('domain-url');
34 | json = $.parseJSON(json);
35 | if(json.state == 'SUCCESS'){
36 | $('input[up-id='+ from+']').val(json.url);
37 | $('.'+ from).html($('
').attr('src', domainUrl+json.url));
38 | }else{
39 | alert(json.state);
40 | }
41 | }
42 | });
43 | });
44 |
45 | })(jQuery)
--------------------------------------------------------------------------------
/statics/css/upload.css:
--------------------------------------------------------------------------------
1 | .per_upload_con {
2 | width: 462px;
3 | height: 148px;
4 | position: relative;
5 | }
6 | .per_upload_img {
7 | width: 218px;
8 | height: 146px;
9 | background: url(../images/bg.jpg) no-repeat center center;
10 | border: 1px solid #e4e4e4;
11 | cursor: pointer;
12 | float: left;
13 | margin-right: 12px;
14 | color: #999999;
15 | line-height: 146px;
16 | text-align: center;
17 | }
18 | .per_upload_img.empty {
19 | background: url(../images/white.png) repeat center center;
20 | }
21 | .per_real_img {
22 | position: absolute;
23 | width: 218px;
24 | height: 146px;
25 | top: 1px;
26 | left: 1px;
27 | overflow: hidden;
28 | cursor: pointer;
29 | }
30 | .per_real_img img{
31 | width: 100%;
32 | height:100%;
33 | }
34 | .per_upload_img:hover {
35 | border-color: #f3a948;
36 | color: #ff7624;
37 | }
38 | .per_upload_img:hover .plus, .per_upload_img:hover .plus_text {
39 | color: #ff7624;
40 | }
41 | .per_upload_text {
42 | width: 216px;
43 | float: left;
44 | padding-top: 12px;
45 | }
46 | .per_upload_con .red {
47 | color: #ff6702;
48 | }
49 | .per_upload_con .upname {
50 | color: #1f1d1c;
51 | line-height: 12px;
52 | }
53 | .per_upload_con .rule {
54 | color: #999999;
55 | line-height: 22px;
56 | }
57 | .per_upload_con .plus {
58 | font-size: 100px;
59 | color: #b8b8b8;
60 | line-height: 100px;
61 | }
62 | .per_upload_con .plus_text {
63 | color: #999999;
64 | line-height: 22px;
65 | }
66 | .green{
67 | background-color: #35aa47;
68 | color: white;
69 | text-shadow: none;
70 | }
--------------------------------------------------------------------------------
/statics/js/upload.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | var settings = {
4 | fileType : {
5 | img : ['jpg','JPG','jpeg','JPEG','gif','GIF','PNG','png'],
6 | zip : ['zip','ZIP','rar','RAR']
7 | },
8 | maxSize : 30*1024*1024,
9 | url : '/default/certificate',
10 | sucFn : function(){console.log('success')}
11 | };
12 |
13 | $.fn.extend({
14 |
15 | upload:function(options){
16 | var that = this;
17 | settings = $.extend(settings,options);
18 |
19 | //验证文件后缀
20 | function checkType(){
21 | var filepath = that.val(),
22 | filetype = that.attr('filetype'),
23 | thisType = filepath.split('.').pop();
24 | if(filepath){
25 | var pass = false;
26 | $.each(settings.fileType[filetype],function(key,val){
27 | if(thisType == val){
28 | pass = true;
29 | return pass;
30 | }
31 | });
32 | return pass;
33 | }
34 | }
35 |
36 | //验证文件格式
37 | if(!checkType()){
38 | alert('文件格式不正确');
39 | return false;
40 | }
41 |
42 | //验证文件大小
43 | var fileSize = that[0].files[0].size;
44 | if(fileSize > settings.maxSize){
45 | alert('文件超出规定大小');
46 | return false;
47 | }
48 |
49 | //上传相关
50 | var thisId = that.attr('id');
51 | var options={
52 | url:settings.url,
53 | type:"post",
54 | success:settings.sucFn
55 | };
56 | if(!that.hasClass('inited')){
57 | that.addClass('inited')
58 | $("#form_"+thisId).submit(function() {
59 | $(this).ajaxSubmit(options);
60 | return false;
61 | });
62 | }
63 | $("#form_"+thisId).submit();
64 |
65 | }
66 |
67 | });
68 | })(jQuery);
--------------------------------------------------------------------------------
/FileUpload.php:
--------------------------------------------------------------------------------
1 |
5 | * 图片上传组件
6 | * 如何配置请到官网(Yii中文网)查看相关文章
7 | */
8 | namespace common\widgets\file_upload;
9 |
10 | use Yii;
11 | use yii\widgets\InputWidget;
12 | use yii\helpers\Html;
13 | use yii\web\View;
14 | use common\widgets\file_upload\assets\FileUploadAsset;
15 | use yii\helpers\ArrayHelper;
16 | use yii\helpers\Url;
17 |
18 | class FileUpload extends InputWidget
19 | {
20 | public $config = [];
21 |
22 | public $value = '';
23 |
24 | public function init()
25 | {
26 | $_config = [
27 | 'serverUrl' => Url::to(['upload','action'=>'uploadimage']), //上传服务器地址
28 | 'fileName' => 'upfile', //提交的图片表单名称
29 | 'domain_url' => '', //图片域名 不填为当前域名
30 | ];
31 | $this->config = ArrayHelper::merge($_config, $this->config);
32 | }
33 |
34 | public function run()
35 | {
36 | $this->registerClientScript();
37 | if ($this->hasModel()) {
38 | $inputName = Html::getInputName($this->model, $this->attribute);
39 | $inputValue = Html::getAttributeValue($this->model, $this->attribute);
40 | return $this->render('index',[
41 | 'config'=>$this->config,
42 | 'inputName' => $inputName,
43 | 'inputValue' => $inputValue,
44 | 'attribute' => $this->attribute,
45 | ]);
46 | } else {
47 | return $this->render('index',[
48 | 'config'=>$this->config,
49 | 'inputName' => 'file-upload',
50 | 'inputValue'=> $this->value
51 | ]);
52 | }
53 | }
54 |
55 | public function registerClientScript()
56 | {
57 | FileUploadAsset::register($this->view);
58 | //$script = "FormFileUpload.init();";
59 | //$this->view->registerJs($script, View::POS_READY);
60 | }
61 | }
--------------------------------------------------------------------------------
/config.php:
--------------------------------------------------------------------------------
1 |
5 | * 图片上传组件
6 | * 如何配置请到官网(Yii中文网)查看相关文章
7 | */
8 | return [
9 | /* 上传图片配置项 */
10 | "imageActionName" => "uploadimage", /* 执行上传图片的action名称 */
11 | "imageFieldName" => "upfile", /* 提交的图片表单名称 */
12 | "imageMaxSize" => 2048000, /* 上传大小限制,单位B */
13 | "imageAllowFiles"=> [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
14 | "imageCompressEnable"=> true, /* 是否压缩图片,默认是true */
15 | "imageCompressBorder"=> 1600, /* 图片压缩最长边限制 */
16 | "imageInsertAlign"=> "none", /* 插入的图片浮动方式 */
17 | "imageUrlPrefix"=> "", /* 图片访问路径前缀 */
18 | "imagePathFormat"=> "/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
19 | "uploadFilePath" => "", /* 文件保存绝对路径 */
20 | /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
21 | /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
22 | /* {time} 会替换成时间戳 */
23 | /* {yyyy} 会替换成四位年份 */
24 | /* {yy} 会替换成两位年份 */
25 | /* {mm} 会替换成两位月份 */
26 | /* {dd} 会替换成两位日期 */
27 | /* {hh} 会替换成两位小时 */
28 | /* {ii} 会替换成两位分钟 */
29 | /* {ss} 会替换成两位秒 */
30 | /* 非法字符 \ : * ? " < > | */
31 | /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
32 |
33 |
34 | /* 上传文件配置 */
35 | "fileActionName"=> "uploadfile", /* controller里,执行上传视频的action名称 */
36 | "fileFieldName"=> "upfile", /* 提交的文件表单名称 */
37 | "filePathFormat"=> "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
38 | "fileUrlPrefix"=> "", /* 文件访问路径前缀 */
39 | "fileMaxSize"=> 51200000, /* 上传大小限制,单位B,默认50MB */
40 | "fileAllowFiles"=> [
41 | ".png", ".jpg", ".jpeg", ".gif", ".bmp",
42 | ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
43 | ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
44 | ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
45 | ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
46 | ], /* 上传文件格式显示 */
47 |
48 | ];
--------------------------------------------------------------------------------
/UploadAction.php:
--------------------------------------------------------------------------------
1 |
6 | * 图片上传组件
7 | * 如何配置请到官网(Yii中文网)查看相关文章
8 | */
9 |
10 | use Yii;
11 | use yii\base\Action;
12 | use yii\helpers\ArrayHelper;
13 | use common\widgets\file_upload\Uploader;
14 |
15 | class UploadAction extends Action
16 | {
17 | /**
18 | * 配置文件
19 | * @var array
20 | */
21 | public $config = [];
22 |
23 | public function init()
24 | {
25 | //close csrf
26 | Yii::$app->request->enableCsrfValidation = false;
27 | //默认设置
28 | $_config = require(__DIR__ . '/config.php');
29 | //load config file
30 | $this->config = ArrayHelper::merge($_config, $this->config);
31 | parent::init();
32 | }
33 |
34 | public function run()
35 | {
36 | $action = Yii::$app->request->get('action');
37 | switch ($action) {
38 | /* 上传图片 */
39 | case 'uploadimage':
40 | /* 上传文件 */
41 | case 'uploadfile':
42 | $result = $this->ActUpload();
43 | break;
44 | default:
45 | $result = json_encode(array(
46 | 'state' => '请求地址出错'
47 | ));
48 | break;
49 | }
50 | echo $result;
51 | }
52 |
53 | /**
54 | * 上传
55 | * @return string
56 | */
57 | protected function ActUpload()
58 | {
59 | $base64 = "upload";
60 | switch (htmlspecialchars($_GET['action'])) {
61 |
62 | case 'uploadimage':
63 | $config = array(
64 | "pathFormat" => $this->config['imagePathFormat'],
65 | "maxSize" => $this->config['imageMaxSize'],
66 | "allowFiles" => $this->config['imageAllowFiles'],
67 | );
68 | $fieldName = $this->config['imageFieldName'];
69 | break;
70 |
71 | case 'uploadfile':
72 | default:
73 | $config = array(
74 | "pathFormat" => $this->config['filePathFormat'],
75 | "maxSize" => $this->config['fileMaxSize'],
76 | "allowFiles" => $this->config['fileAllowFiles']
77 | );
78 | $fieldName = $this->config['fileFieldName'];
79 | break;
80 | }
81 | $config['uploadFilePath'] = isset($this->config['uploadFilePath'])?$this->config['uploadFilePath']:'';
82 | /* 生成上传实例对象并完成上传 */
83 | $up = new Uploader($fieldName, $config, $base64);
84 | /**
85 | * 得到上传文件所对应的各个参数,数组结构
86 | * array(
87 | * "state" => "", //上传状态,上传成功时必须返回"SUCCESS"
88 | * "url" => "", //返回的地址
89 | * "title" => "", //新文件名
90 | * "original" => "", //原始文件名
91 | * "type" => "" //文件类型
92 | * "size" => "", //文件大小
93 | * )
94 | */
95 | /* 返回数据 */
96 | return json_encode($up->getFileInfo());
97 | }
98 | }
--------------------------------------------------------------------------------
/Uploader.php:
--------------------------------------------------------------------------------
1 |
6 | * 图片上传组件
7 | * 如何配置请到官网(Yii中文网)查看相关文章
8 | */
9 |
10 | class Uploader
11 | {
12 | private $fileField; //文件域名
13 | private $file; //文件上传对象
14 | private $base64; //文件上传对象
15 | private $config; //配置信息
16 | private $oriName; //原始文件名
17 | private $fileName; //新文件名
18 | private $fullName; //完整文件名,即从当前配置目录开始的URL
19 | private $filePath; //完整文件名,即从当前配置目录开始的URL
20 | private $fileSize; //文件大小
21 | private $fileType; //文件类型
22 | private $stateInfo; //上传状态信息,
23 | private $stateMap = array( //上传状态映射表,国际化用户需考虑此处数据的国际化
24 | "SUCCESS", //上传成功标记,在UEditor中内不可改变,否则flash判断会出错
25 | "文件大小超出 upload_max_filesize 限制",
26 | "文件大小超出 MAX_FILE_SIZE 限制",
27 | "文件未被完整上传",
28 | "没有文件被上传",
29 | "上传文件为空",
30 | "ERROR_TMP_FILE" => "临时文件错误",
31 | "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
32 | "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
33 | "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
34 | "ERROR_CREATE_DIR" => "目录创建失败",
35 | "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
36 | "ERROR_FILE_MOVE" => "文件保存时出错",
37 | "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
38 | "ERROR_WRITE_CONTENT" => "写入文件内容错误",
39 | "ERROR_UNKNOWN" => "未知错误",
40 | "ERROR_DEAD_LINK" => "链接不可用",
41 | "ERROR_HTTP_LINK" => "链接不是http链接",
42 | "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确"
43 | );
44 | /**
45 | * 构造函数
46 | * @param $fileField 表单名称
47 | * @param $config 配置项
48 | * @param string $type 是否解析base64编码,可省略。若开启,则$fileField代表的是base64编码的字符串表单名
49 | */
50 | public function __construct($fileField, $config, $type = "upload")
51 | {
52 | $this->fileField = $fileField;
53 | $this->config = $config;
54 | $this->type = $type;
55 | if ($type == "remote") {
56 | $this->saveRemote();
57 | } else if ($type == "base64") {
58 | $this->upBase64();
59 | } else {
60 | $this->upFile();
61 | }
62 | // $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']);
63 | }
64 | /**
65 | * 上传文件的主处理方法
66 | * @return mixed
67 | */
68 | private function upFile()
69 | {
70 | $file = $this->file = $_FILES[$this->fileField];
71 | if (!$file) {
72 | $this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND");
73 | return;
74 | }
75 | if ($this->file['error']) {
76 | $this->stateInfo = $this->getStateInfo($file['error']);
77 | return;
78 | } else if (!file_exists($file['tmp_name'])) {
79 | $this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND");
80 | return;
81 | } else if (!is_uploaded_file($file['tmp_name'])) {
82 | $this->stateInfo = $this->getStateInfo("ERROR_TMPFILE");
83 | return;
84 | }
85 | $this->oriName = $file['name'];
86 | $this->fileSize = $file['size'];
87 | $this->fileType = $this->getFileExt();
88 | $this->fullName = $this->getFullName();
89 | $this->filePath = $this->getFilePath();
90 | $this->fileName = $this->getFileName();
91 | $dirname = dirname($this->filePath);
92 | //检查文件大小是否超出限制
93 | if (!$this->checkSize()) {
94 | $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
95 | return;
96 | }
97 | //检查是否不允许的文件格式
98 | if (!$this->checkType()) {
99 | $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
100 | return;
101 | }
102 | //创建目录失败
103 | if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
104 | $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
105 | return;
106 | } else if (!is_writeable($dirname)) {
107 | $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
108 | return;
109 | }
110 | //移动文件
111 | if (!(move_uploaded_file($file["tmp_name"], $this->filePath) && file_exists($this->filePath))) { //移动失败
112 | $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
113 | } else { //移动成功
114 | $this->stateInfo = $this->stateMap[0];
115 | }
116 | }
117 | /**
118 | * 处理base64编码的图片上传
119 | * @return mixed
120 | */
121 | private function upBase64()
122 | {
123 | $base64Data = $_POST[$this->fileField];
124 | $img = base64_decode($base64Data);
125 | $this->oriName = $this->config['oriName'];
126 | $this->fileSize = strlen($img);
127 | $this->fileType = $this->getFileExt();
128 | $this->fullName = $this->getFullName();
129 | $this->filePath = $this->getFilePath();
130 | $this->fileName = $this->getFileName();
131 | $dirname = dirname($this->filePath);
132 | //检查文件大小是否超出限制
133 | if (!$this->checkSize()) {
134 | $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
135 | return;
136 | }
137 | //创建目录失败
138 | if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
139 | $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
140 | return;
141 | } else if (!is_writeable($dirname)) {
142 | $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
143 | return;
144 | }
145 | //移动文件
146 | if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
147 | $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
148 | } else { //移动成功
149 | $this->stateInfo = $this->stateMap[0];
150 | }
151 | }
152 | /**
153 | * 拉取远程图片
154 | * @return mixed
155 | */
156 | private function saveRemote()
157 | {
158 | $imgUrl = htmlspecialchars($this->fileField);
159 | $imgUrl = str_replace("&", "&", $imgUrl);
160 | //http开头验证
161 | if (strpos($imgUrl, "http") !== 0) {
162 | $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
163 | return;
164 | }
165 | //获取请求头并检测死链
166 | $heads = get_headers($imgUrl);
167 | if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
168 | $this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK");
169 | return;
170 | }
171 | //格式验证(扩展名验证和Content-Type验证)
172 | $fileType = strtolower(strrchr($imgUrl, '.'));
173 | if (!in_array($fileType, $this->config['allowFiles']) || stristr($heads['Content-Type'], "image")) {
174 | $this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE");
175 | return;
176 | }
177 | //打开输出缓冲区并获取远程图片
178 | ob_start();
179 | $context = stream_context_create(
180 | array('http' => array(
181 | 'follow_location' => false // don't follow redirects
182 | ))
183 | );
184 | readfile($imgUrl, false, $context);
185 | $img = ob_get_contents();
186 | ob_end_clean();
187 | preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $m);
188 | $this->oriName = $m ? $m[1] : "";
189 | $this->fileSize = strlen($img);
190 | $this->fileType = $this->getFileExt();
191 | $this->fullName = $this->getFullName();
192 | $this->filePath = $this->getFilePath();
193 | $this->fileName = $this->getFileName();
194 | $dirname = dirname($this->filePath);
195 | //检查文件大小是否超出限制
196 | if (!$this->checkSize()) {
197 | $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
198 | return;
199 | }
200 | //创建目录失败
201 | if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
202 | $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
203 | return;
204 | } else if (!is_writeable($dirname)) {
205 | $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
206 | return;
207 | }
208 | //移动文件
209 | if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
210 | $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
211 | } else { //移动成功
212 | $this->stateInfo = $this->stateMap[0];
213 | }
214 | }
215 | /**
216 | * 上传错误检查
217 | * @param $errCode
218 | * @return string
219 | */
220 | private function getStateInfo($errCode)
221 | {
222 | return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
223 | }
224 | /**
225 | * 获取文件扩展名
226 | * @return string
227 | */
228 | private function getFileExt()
229 | {
230 | return strtolower(strrchr($this->oriName, '.'));
231 | }
232 | /**
233 | * 重命名文件
234 | * @return string
235 | */
236 | private function getFullName()
237 | {
238 | //替换日期事件
239 | $t = time();
240 | $d = explode('-', date("Y-y-m-d-H-i-s"));
241 | $format = $this->config["pathFormat"];
242 | $format = str_replace("{yyyy}", $d[0], $format);
243 | $format = str_replace("{yy}", $d[1], $format);
244 | $format = str_replace("{mm}", $d[2], $format);
245 | $format = str_replace("{dd}", $d[3], $format);
246 | $format = str_replace("{hh}", $d[4], $format);
247 | $format = str_replace("{ii}", $d[5], $format);
248 | $format = str_replace("{ss}", $d[6], $format);
249 | $format = str_replace("{time}", $t, $format);
250 | //过滤文件名的非法自负,并替换文件名
251 | $oriName = substr($this->oriName, 0, strrpos($this->oriName, '.'));
252 | $oriName = preg_replace("/[\|\?\"\<\>\/\*\\\\]+/", '', $oriName);
253 | $format = str_replace("{filename}", $oriName, $format);
254 | //替换随机字符串
255 | $randNum = rand(1, 10000000000) . rand(1, 10000000000);
256 | if (preg_match("/\{rand\:([\d]*)\}/i", $format, $matches)) {
257 | $format = preg_replace("/\{rand\:[\d]*\}/i", substr($randNum, 0, $matches[1]), $format);
258 | }
259 | $ext = $this->getFileExt();
260 | return $format . $ext;
261 | }
262 | /**
263 | * 获取文件名
264 | * @return string
265 | */
266 | private function getFileName()
267 | {
268 | return substr($this->filePath, strrpos($this->filePath, '/') + 1);
269 | }
270 | /**
271 | * 获取文件完整路径
272 | * @return string
273 | */
274 | private function getFilePath()
275 | {
276 | $fullname = $this->fullName;
277 | $rootPath = isset($this->config['uploadFilePath'])&&!empty($this->config['uploadFilePath'])?$this->config['uploadFilePath']:$_SERVER['DOCUMENT_ROOT'];
278 | if (substr($fullname, 0, 1) != '/') {
279 | $fullname = '/' . $fullname;
280 | }
281 | return $rootPath . $fullname;
282 | }
283 |
284 | /**
285 | * 文件类型检测
286 | * @return bool
287 | */
288 | private function checkType()
289 | {
290 | return in_array($this->getFileExt(), $this->config["allowFiles"]);
291 | }
292 | /**
293 | * 文件大小检测
294 | * @return bool
295 | */
296 | private function checkSize()
297 | {
298 | return $this->fileSize <= ($this->config["maxSize"]);
299 | }
300 | /**
301 | * 获取当前上传成功文件的各项信息
302 | * @return array
303 | */
304 | public function getFileInfo()
305 | {
306 | return array(
307 | "state" => $this->stateInfo,
308 | "url" => $this->fullName,
309 | "title" => $this->fileName,
310 | "original" => $this->oriName,
311 | "type" => $this->fileType,
312 | "size" => $this->fileSize,
313 | );
314 | }
315 | }
--------------------------------------------------------------------------------
/statics/js/jquery.form.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Form Plugin
3 | * version: 3.51.0-2014.06.20
4 | * Requires jQuery v1.5 or later
5 | * Copyright (c) 2014 M. Alsup
6 | * Examples and documentation at: http://malsup.com/jquery/form/
7 | * Project repository: https://github.com/malsup/form
8 | * Dual licensed under the MIT and GPL licenses.
9 | * https://github.com/malsup/form#copyright-and-license
10 | */
11 | /*global ActiveXObject */
12 |
13 | // AMD support
14 | (function (factory) {
15 | "use strict";
16 | if (typeof define === 'function' && define.amd) {
17 | // using AMD; register as anon module
18 | define(['jquery'], factory);
19 | } else {
20 | // no AMD; invoke directly
21 | factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
22 | }
23 | }
24 |
25 | (function($) {
26 | "use strict";
27 |
28 | /*
29 | Usage Note:
30 | -----------
31 | Do not use both ajaxSubmit and ajaxForm on the same form. These
32 | functions are mutually exclusive. Use ajaxSubmit if you want
33 | to bind your own submit handler to the form. For example,
34 |
35 | $(document).ready(function() {
36 | $('#myForm').on('submit', function(e) {
37 | e.preventDefault(); // <-- important
38 | $(this).ajaxSubmit({
39 | target: '#output'
40 | });
41 | });
42 | });
43 |
44 | Use ajaxForm when you want the plugin to manage all the event binding
45 | for you. For example,
46 |
47 | $(document).ready(function() {
48 | $('#myForm').ajaxForm({
49 | target: '#output'
50 | });
51 | });
52 |
53 | You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
54 | form does not have to exist when you invoke ajaxForm:
55 |
56 | $('#myForm').ajaxForm({
57 | delegation: true,
58 | target: '#output'
59 | });
60 |
61 | When using ajaxForm, the ajaxSubmit function will be invoked for you
62 | at the appropriate time.
63 | */
64 |
65 | /**
66 | * Feature detection
67 | */
68 | var feature = {};
69 | feature.fileapi = $("").get(0).files !== undefined;
70 | feature.formdata = window.FormData !== undefined;
71 |
72 | var hasProp = !!$.fn.prop;
73 |
74 | // attr2 uses prop when it can but checks the return type for
75 | // an expected string. this accounts for the case where a form
76 | // contains inputs with names like "action" or "method"; in those
77 | // cases "prop" returns the element
78 | $.fn.attr2 = function() {
79 | if ( ! hasProp ) {
80 | return this.attr.apply(this, arguments);
81 | }
82 | var val = this.prop.apply(this, arguments);
83 | if ( ( val && val.jquery ) || typeof val === 'string' ) {
84 | return val;
85 | }
86 | return this.attr.apply(this, arguments);
87 | };
88 |
89 | /**
90 | * ajaxSubmit() provides a mechanism for immediately submitting
91 | * an HTML form using AJAX.
92 | */
93 | $.fn.ajaxSubmit = function(options) {
94 | /*jshint scripturl:true */
95 |
96 | // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
97 | if (!this.length) {
98 | log('ajaxSubmit: skipping submit process - no element selected');
99 | return this;
100 | }
101 |
102 | var method, action, url, $form = this;
103 |
104 | if (typeof options == 'function') {
105 | options = { success: options };
106 | }
107 | else if ( options === undefined ) {
108 | options = {};
109 | }
110 |
111 | method = options.type || this.attr2('method');
112 | action = options.url || this.attr2('action');
113 |
114 | url = (typeof action === 'string') ? $.trim(action) : '';
115 | url = url || window.location.href || '';
116 | if (url) {
117 | // clean url (don't include hash vaue)
118 | url = (url.match(/^([^#]+)/)||[])[1];
119 | }
120 |
121 | options = $.extend(true, {
122 | url: url,
123 | success: $.ajaxSettings.success,
124 | type: method || $.ajaxSettings.type,
125 | iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
126 | }, options);
127 |
128 | // hook for manipulating the form data before it is extracted;
129 | // convenient for use with rich editors like tinyMCE or FCKEditor
130 | var veto = {};
131 | this.trigger('form-pre-serialize', [this, options, veto]);
132 | if (veto.veto) {
133 | log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
134 | return this;
135 | }
136 |
137 | // provide opportunity to alter form data before it is serialized
138 | if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
139 | log('ajaxSubmit: submit aborted via beforeSerialize callback');
140 | return this;
141 | }
142 |
143 | var traditional = options.traditional;
144 | if ( traditional === undefined ) {
145 | traditional = $.ajaxSettings.traditional;
146 | }
147 |
148 | var elements = [];
149 | var qx, a = this.formToArray(options.semantic, elements);
150 | if (options.data) {
151 | options.extraData = options.data;
152 | qx = $.param(options.data, traditional);
153 | }
154 |
155 | // give pre-submit callback an opportunity to abort the submit
156 | if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
157 | log('ajaxSubmit: submit aborted via beforeSubmit callback');
158 | return this;
159 | }
160 |
161 | // fire vetoable 'validate' event
162 | this.trigger('form-submit-validate', [a, this, options, veto]);
163 | if (veto.veto) {
164 | log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
165 | return this;
166 | }
167 |
168 | var q = $.param(a, traditional);
169 | if (qx) {
170 | q = ( q ? (q + '&' + qx) : qx );
171 | }
172 | if (options.type.toUpperCase() == 'GET') {
173 | options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
174 | options.data = null; // data is null for 'get'
175 | }
176 | else {
177 | options.data = q; // data is the query string for 'post'
178 | }
179 |
180 | var callbacks = [];
181 | if (options.resetForm) {
182 | callbacks.push(function() { $form.resetForm(); });
183 | }
184 | if (options.clearForm) {
185 | callbacks.push(function() { $form.clearForm(options.includeHidden); });
186 | }
187 |
188 | // perform a load on the target only if dataType is not provided
189 | if (!options.dataType && options.target) {
190 | var oldSuccess = options.success || function(){};
191 | callbacks.push(function(data) {
192 | var fn = options.replaceTarget ? 'replaceWith' : 'html';
193 | $(options.target)[fn](data).each(oldSuccess, arguments);
194 | });
195 | }
196 | else if (options.success) {
197 | callbacks.push(options.success);
198 | }
199 |
200 | options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
201 | var context = options.context || this ; // jQuery 1.4+ supports scope context
202 | for (var i=0, max=callbacks.length; i < max; i++) {
203 | callbacks[i].apply(context, [data, status, xhr || $form, $form]);
204 | }
205 | };
206 |
207 | if (options.error) {
208 | var oldError = options.error;
209 | options.error = function(xhr, status, error) {
210 | var context = options.context || this;
211 | oldError.apply(context, [xhr, status, error, $form]);
212 | };
213 | }
214 |
215 | if (options.complete) {
216 | var oldComplete = options.complete;
217 | options.complete = function(xhr, status) {
218 | var context = options.context || this;
219 | oldComplete.apply(context, [xhr, status, $form]);
220 | };
221 | }
222 |
223 | // are there files to upload?
224 |
225 | // [value] (issue #113), also see comment:
226 | // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
227 | var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
228 |
229 | var hasFileInputs = fileInputs.length > 0;
230 | var mp = 'multipart/form-data';
231 | var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
232 |
233 | var fileAPI = feature.fileapi && feature.formdata;
234 | log("fileAPI :" + fileAPI);
235 | var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
236 |
237 | var jqxhr;
238 |
239 | // options.iframe allows user to force iframe mode
240 | // 06-NOV-09: now defaulting to iframe mode if file input is detected
241 | if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
242 | // hack to fix Safari hang (thanks to Tim Molendijk for this)
243 | // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
244 | if (options.closeKeepAlive) {
245 | $.get(options.closeKeepAlive, function() {
246 | jqxhr = fileUploadIframe(a);
247 | });
248 | }
249 | else {
250 | jqxhr = fileUploadIframe(a);
251 | }
252 | }
253 | else if ((hasFileInputs || multipart) && fileAPI) {
254 | jqxhr = fileUploadXhr(a);
255 | }
256 | else {
257 | jqxhr = $.ajax(options);
258 | }
259 |
260 | $form.removeData('jqxhr').data('jqxhr', jqxhr);
261 |
262 | // clear element array
263 | for (var k=0; k < elements.length; k++) {
264 | elements[k] = null;
265 | }
266 |
267 | // fire 'notify' event
268 | this.trigger('form-submit-notify', [this, options]);
269 | return this;
270 |
271 | // utility fn for deep serialization
272 | function deepSerialize(extraData){
273 | var serialized = $.param(extraData, options.traditional).split('&');
274 | var len = serialized.length;
275 | var result = [];
276 | var i, part;
277 | for (i=0; i < len; i++) {
278 | // #252; undo param space replacement
279 | serialized[i] = serialized[i].replace(/\+/g,' ');
280 | part = serialized[i].split('=');
281 | // #278; use array instead of object storage, favoring array serializations
282 | result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
283 | }
284 | return result;
285 | }
286 |
287 | // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
288 | function fileUploadXhr(a) {
289 | var formdata = new FormData();
290 |
291 | for (var i=0; i < a.length; i++) {
292 | formdata.append(a[i].name, a[i].value);
293 | }
294 |
295 | if (options.extraData) {
296 | var serializedData = deepSerialize(options.extraData);
297 | for (i=0; i < serializedData.length; i++) {
298 | if (serializedData[i]) {
299 | formdata.append(serializedData[i][0], serializedData[i][1]);
300 | }
301 | }
302 | }
303 |
304 | options.data = null;
305 |
306 | var s = $.extend(true, {}, $.ajaxSettings, options, {
307 | contentType: false,
308 | processData: false,
309 | cache: false,
310 | type: method || 'POST'
311 | });
312 |
313 | if (options.uploadProgress) {
314 | // workaround because jqXHR does not expose upload property
315 | s.xhr = function() {
316 | var xhr = $.ajaxSettings.xhr();
317 | if (xhr.upload) {
318 | xhr.upload.addEventListener('progress', function(event) {
319 | var percent = 0;
320 | var position = event.loaded || event.position; /*event.position is deprecated*/
321 | var total = event.total;
322 | if (event.lengthComputable) {
323 | percent = Math.ceil(position / total * 100);
324 | }
325 | options.uploadProgress(event, position, total, percent);
326 | }, false);
327 | }
328 | return xhr;
329 | };
330 | }
331 |
332 | s.data = null;
333 | var beforeSend = s.beforeSend;
334 | s.beforeSend = function(xhr, o) {
335 | //Send FormData() provided by user
336 | if (options.formData) {
337 | o.data = options.formData;
338 | }
339 | else {
340 | o.data = formdata;
341 | }
342 | if(beforeSend) {
343 | beforeSend.call(this, xhr, o);
344 | }
345 | };
346 | return $.ajax(s);
347 | }
348 |
349 | // private function for handling file uploads (hat tip to YAHOO!)
350 | function fileUploadIframe(a) {
351 | var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
352 | var deferred = $.Deferred();
353 |
354 | // #341
355 | deferred.abort = function(status) {
356 | xhr.abort(status);
357 | };
358 |
359 | if (a) {
360 | // ensure that every serialized input is still enabled
361 | for (i=0; i < elements.length; i++) {
362 | el = $(elements[i]);
363 | if ( hasProp ) {
364 | el.prop('disabled', false);
365 | }
366 | else {
367 | el.removeAttr('disabled');
368 | }
369 | }
370 | }
371 |
372 | s = $.extend(true, {}, $.ajaxSettings, options);
373 | s.context = s.context || s;
374 | id = 'jqFormIO' + (new Date().getTime());
375 | if (s.iframeTarget) {
376 | $io = $(s.iframeTarget);
377 | n = $io.attr2('name');
378 | if (!n) {
379 | $io.attr2('name', id);
380 | }
381 | else {
382 | id = n;
383 | }
384 | }
385 | else {
386 | $io = $('');
387 | $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
388 | }
389 | io = $io[0];
390 |
391 |
392 | xhr = { // mock object
393 | aborted: 0,
394 | responseText: null,
395 | responseXML: null,
396 | status: 0,
397 | statusText: 'n/a',
398 | getAllResponseHeaders: function() {},
399 | getResponseHeader: function() {},
400 | setRequestHeader: function() {},
401 | abort: function(status) {
402 | var e = (status === 'timeout' ? 'timeout' : 'aborted');
403 | log('aborting upload... ' + e);
404 | this.aborted = 1;
405 |
406 | try { // #214, #257
407 | if (io.contentWindow.document.execCommand) {
408 | io.contentWindow.document.execCommand('Stop');
409 | }
410 | }
411 | catch(ignore) {}
412 |
413 | $io.attr('src', s.iframeSrc); // abort op in progress
414 | xhr.error = e;
415 | if (s.error) {
416 | s.error.call(s.context, xhr, e, status);
417 | }
418 | if (g) {
419 | $.event.trigger("ajaxError", [xhr, s, e]);
420 | }
421 | if (s.complete) {
422 | s.complete.call(s.context, xhr, e);
423 | }
424 | }
425 | };
426 |
427 | g = s.global;
428 | // trigger ajax global events so that activity/block indicators work like normal
429 | if (g && 0 === $.active++) {
430 | $.event.trigger("ajaxStart");
431 | }
432 | if (g) {
433 | $.event.trigger("ajaxSend", [xhr, s]);
434 | }
435 |
436 | if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
437 | if (s.global) {
438 | $.active--;
439 | }
440 | deferred.reject();
441 | return deferred;
442 | }
443 | if (xhr.aborted) {
444 | deferred.reject();
445 | return deferred;
446 | }
447 |
448 | // add submitting element to data if we know it
449 | sub = form.clk;
450 | if (sub) {
451 | n = sub.name;
452 | if (n && !sub.disabled) {
453 | s.extraData = s.extraData || {};
454 | s.extraData[n] = sub.value;
455 | if (sub.type == "image") {
456 | s.extraData[n+'.x'] = form.clk_x;
457 | s.extraData[n+'.y'] = form.clk_y;
458 | }
459 | }
460 | }
461 |
462 | var CLIENT_TIMEOUT_ABORT = 1;
463 | var SERVER_ABORT = 2;
464 |
465 | function getDoc(frame) {
466 | /* it looks like contentWindow or contentDocument do not
467 | * carry the protocol property in ie8, when running under ssl
468 | * frame.document is the only valid response document, since
469 | * the protocol is know but not on the other two objects. strange?
470 | * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
471 | */
472 |
473 | var doc = null;
474 |
475 | // IE8 cascading access check
476 | try {
477 | if (frame.contentWindow) {
478 | doc = frame.contentWindow.document;
479 | }
480 | } catch(err) {
481 | // IE8 access denied under ssl & missing protocol
482 | log('cannot get iframe.contentWindow document: ' + err);
483 | }
484 |
485 | if (doc) { // successful getting content
486 | return doc;
487 | }
488 |
489 | try { // simply checking may throw in ie8 under ssl or mismatched protocol
490 | doc = frame.contentDocument ? frame.contentDocument : frame.document;
491 | } catch(err) {
492 | // last attempt
493 | log('cannot get iframe.contentDocument: ' + err);
494 | doc = frame.document;
495 | }
496 | return doc;
497 | }
498 |
499 | // Rails CSRF hack (thanks to Yvan Barthelemy)
500 | var csrf_token = $('meta[name=csrf-token]').attr('content');
501 | var csrf_param = $('meta[name=csrf-param]').attr('content');
502 | if (csrf_param && csrf_token) {
503 | s.extraData = s.extraData || {};
504 | s.extraData[csrf_param] = csrf_token;
505 | }
506 |
507 | // take a breath so that pending repaints get some cpu time before the upload starts
508 | function doSubmit() {
509 | // make sure form attrs are set
510 | var t = $form.attr2('target'),
511 | a = $form.attr2('action'),
512 | mp = 'multipart/form-data',
513 | et = $form.attr('enctype') || $form.attr('encoding') || mp;
514 |
515 | // update form attrs in IE friendly way
516 | form.setAttribute('target',id);
517 | if (!method || /post/i.test(method) ) {
518 | form.setAttribute('method', 'POST');
519 | }
520 | if (a != s.url) {
521 | form.setAttribute('action', s.url);
522 | }
523 |
524 | // ie borks in some cases when setting encoding
525 | if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
526 | $form.attr({
527 | encoding: 'multipart/form-data',
528 | enctype: 'multipart/form-data'
529 | });
530 | }
531 |
532 | // support timout
533 | if (s.timeout) {
534 | timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
535 | }
536 |
537 | // look for server aborts
538 | function checkState() {
539 | try {
540 | var state = getDoc(io).readyState;
541 | log('state = ' + state);
542 | if (state && state.toLowerCase() == 'uninitialized') {
543 | setTimeout(checkState,50);
544 | }
545 | }
546 | catch(e) {
547 | log('Server abort: ' , e, ' (', e.name, ')');
548 | cb(SERVER_ABORT);
549 | if (timeoutHandle) {
550 | clearTimeout(timeoutHandle);
551 | }
552 | timeoutHandle = undefined;
553 | }
554 | }
555 |
556 | // add "extra" data to form if provided in options
557 | var extraInputs = [];
558 | try {
559 | if (s.extraData) {
560 | for (var n in s.extraData) {
561 | if (s.extraData.hasOwnProperty(n)) {
562 | // if using the $.param format that allows for multiple values with the same name
563 | if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
564 | extraInputs.push(
565 | $('').val(s.extraData[n].value)
566 | .appendTo(form)[0]);
567 | } else {
568 | extraInputs.push(
569 | $('').val(s.extraData[n])
570 | .appendTo(form)[0]);
571 | }
572 | }
573 | }
574 | }
575 |
576 | if (!s.iframeTarget) {
577 | // add iframe to doc and submit the form
578 | $io.appendTo('body');
579 | }
580 | if (io.attachEvent) {
581 | io.attachEvent('onload', cb);
582 | }
583 | else {
584 | io.addEventListener('load', cb, false);
585 | }
586 | setTimeout(checkState,15);
587 |
588 | try {
589 | form.submit();
590 | } catch(err) {
591 | // just in case form has element with name/id of 'submit'
592 | var submitFn = document.createElement('form').submit;
593 | submitFn.apply(form);
594 | }
595 | }
596 | finally {
597 | // reset attrs and remove "extra" input elements
598 | form.setAttribute('action',a);
599 | form.setAttribute('enctype', et); // #380
600 | if(t) {
601 | form.setAttribute('target', t);
602 | } else {
603 | $form.removeAttr('target');
604 | }
605 | $(extraInputs).remove();
606 | }
607 | }
608 |
609 | if (s.forceSync) {
610 | doSubmit();
611 | }
612 | else {
613 | setTimeout(doSubmit, 10); // this lets dom updates render
614 | }
615 |
616 | var data, doc, domCheckCount = 50, callbackProcessed;
617 |
618 | function cb(e) {
619 | if (xhr.aborted || callbackProcessed) {
620 | return;
621 | }
622 |
623 | doc = getDoc(io);
624 | if(!doc) {
625 | log('cannot access response document');
626 | e = SERVER_ABORT;
627 | }
628 | if (e === CLIENT_TIMEOUT_ABORT && xhr) {
629 | xhr.abort('timeout');
630 | deferred.reject(xhr, 'timeout');
631 | return;
632 | }
633 | else if (e == SERVER_ABORT && xhr) {
634 | xhr.abort('server abort');
635 | deferred.reject(xhr, 'error', 'server abort');
636 | return;
637 | }
638 |
639 | if (!doc || doc.location.href == s.iframeSrc) {
640 | // response not received yet
641 | if (!timedOut) {
642 | return;
643 | }
644 | }
645 | if (io.detachEvent) {
646 | io.detachEvent('onload', cb);
647 | }
648 | else {
649 | io.removeEventListener('load', cb, false);
650 | }
651 |
652 | var status = 'success', errMsg;
653 | try {
654 | if (timedOut) {
655 | throw 'timeout';
656 | }
657 |
658 | var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
659 | log('isXml='+isXml);
660 | if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
661 | if (--domCheckCount) {
662 | // in some browsers (Opera) the iframe DOM is not always traversable when
663 | // the onload callback fires, so we loop a bit to accommodate
664 | log('requeing onLoad callback, DOM not available');
665 | setTimeout(cb, 250);
666 | return;
667 | }
668 | // let this fall through because server response could be an empty document
669 | //log('Could not access iframe DOM after mutiple tries.');
670 | //throw 'DOMException: not available';
671 | }
672 |
673 | //log('response detected');
674 | var docRoot = doc.body ? doc.body : doc.documentElement;
675 | xhr.responseText = docRoot ? docRoot.innerHTML : null;
676 | xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
677 | if (isXml) {
678 | s.dataType = 'xml';
679 | }
680 | xhr.getResponseHeader = function(header){
681 | var headers = {'content-type': s.dataType};
682 | return headers[header.toLowerCase()];
683 | };
684 | // support for XHR 'status' & 'statusText' emulation :
685 | if (docRoot) {
686 | xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
687 | xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
688 | }
689 |
690 | var dt = (s.dataType || '').toLowerCase();
691 | var scr = /(json|script|text)/.test(dt);
692 | if (scr || s.textarea) {
693 | // see if user embedded response in textarea
694 | var ta = doc.getElementsByTagName('textarea')[0];
695 | if (ta) {
696 | xhr.responseText = ta.value;
697 | // support for XHR 'status' & 'statusText' emulation :
698 | xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
699 | xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
700 | }
701 | else if (scr) {
702 | // account for browsers injecting pre around json response
703 | var pre = doc.getElementsByTagName('pre')[0];
704 | var b = doc.getElementsByTagName('body')[0];
705 | if (pre) {
706 | xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
707 | }
708 | else if (b) {
709 | xhr.responseText = b.textContent ? b.textContent : b.innerText;
710 | }
711 | }
712 | }
713 | else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
714 | xhr.responseXML = toXml(xhr.responseText);
715 | }
716 |
717 | try {
718 | data = httpData(xhr, dt, s);
719 | }
720 | catch (err) {
721 | status = 'parsererror';
722 | xhr.error = errMsg = (err || status);
723 | }
724 | }
725 | catch (err) {
726 | log('error caught: ',err);
727 | status = 'error';
728 | xhr.error = errMsg = (err || status);
729 | }
730 |
731 | if (xhr.aborted) {
732 | log('upload aborted');
733 | status = null;
734 | }
735 |
736 | if (xhr.status) { // we've set xhr.status
737 | status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
738 | }
739 |
740 | // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
741 | if (status === 'success') {
742 | if (s.success) {
743 | s.success.call(s.context, data, 'success', xhr);
744 | }
745 | deferred.resolve(xhr.responseText, 'success', xhr);
746 | if (g) {
747 | $.event.trigger("ajaxSuccess", [xhr, s]);
748 | }
749 | }
750 | else if (status) {
751 | if (errMsg === undefined) {
752 | errMsg = xhr.statusText;
753 | }
754 | if (s.error) {
755 | s.error.call(s.context, xhr, status, errMsg);
756 | }
757 | deferred.reject(xhr, 'error', errMsg);
758 | if (g) {
759 | $.event.trigger("ajaxError", [xhr, s, errMsg]);
760 | }
761 | }
762 |
763 | if (g) {
764 | $.event.trigger("ajaxComplete", [xhr, s]);
765 | }
766 |
767 | if (g && ! --$.active) {
768 | $.event.trigger("ajaxStop");
769 | }
770 |
771 | if (s.complete) {
772 | s.complete.call(s.context, xhr, status);
773 | }
774 |
775 | callbackProcessed = true;
776 | if (s.timeout) {
777 | clearTimeout(timeoutHandle);
778 | }
779 |
780 | // clean up
781 | setTimeout(function() {
782 | if (!s.iframeTarget) {
783 | $io.remove();
784 | }
785 | else { //adding else to clean up existing iframe response.
786 | $io.attr('src', s.iframeSrc);
787 | }
788 | xhr.responseXML = null;
789 | }, 100);
790 | }
791 |
792 | var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
793 | if (window.ActiveXObject) {
794 | doc = new ActiveXObject('Microsoft.XMLDOM');
795 | doc.async = 'false';
796 | doc.loadXML(s);
797 | }
798 | else {
799 | doc = (new DOMParser()).parseFromString(s, 'text/xml');
800 | }
801 | return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
802 | };
803 | var parseJSON = $.parseJSON || function(s) {
804 | /*jslint evil:true */
805 | return window['eval']('(' + s + ')');
806 | };
807 |
808 | var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
809 |
810 | var ct = xhr.getResponseHeader('content-type') || '',
811 | xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
812 | data = xml ? xhr.responseXML : xhr.responseText;
813 |
814 | if (xml && data.documentElement.nodeName === 'parsererror') {
815 | if ($.error) {
816 | $.error('parsererror');
817 | }
818 | }
819 | if (s && s.dataFilter) {
820 | data = s.dataFilter(data, type);
821 | }
822 | if (typeof data === 'string') {
823 | if (type === 'json' || !type && ct.indexOf('json') >= 0) {
824 | data = parseJSON(data);
825 | } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
826 | $.globalEval(data);
827 | }
828 | }
829 | return data;
830 | };
831 |
832 | return deferred;
833 | }
834 | };
835 |
836 | /**
837 | * ajaxForm() provides a mechanism for fully automating form submission.
838 | *
839 | * The advantages of using this method instead of ajaxSubmit() are:
840 | *
841 | * 1: This method will include coordinates for elements (if the element
842 | * is used to submit the form).
843 | * 2. This method will include the submit element's name/value data (for the element that was
844 | * used to submit the form).
845 | * 3. This method binds the submit() method to the form for you.
846 | *
847 | * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
848 | * passes the options argument along after properly binding events for submit elements and
849 | * the form itself.
850 | */
851 | $.fn.ajaxForm = function(options) {
852 | options = options || {};
853 | options.delegation = options.delegation && $.isFunction($.fn.on);
854 |
855 | // in jQuery 1.3+ we can fix mistakes with the ready state
856 | if (!options.delegation && this.length === 0) {
857 | var o = { s: this.selector, c: this.context };
858 | if (!$.isReady && o.s) {
859 | log('DOM not ready, queuing ajaxForm');
860 | $(function() {
861 | $(o.s,o.c).ajaxForm(options);
862 | });
863 | return this;
864 | }
865 | // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
866 | log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
867 | return this;
868 | }
869 |
870 | if ( options.delegation ) {
871 | $(document)
872 | .off('submit.form-plugin', this.selector, doAjaxSubmit)
873 | .off('click.form-plugin', this.selector, captureSubmittingElement)
874 | .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
875 | .on('click.form-plugin', this.selector, options, captureSubmittingElement);
876 | return this;
877 | }
878 |
879 | return this.ajaxFormUnbind()
880 | .bind('submit.form-plugin', options, doAjaxSubmit)
881 | .bind('click.form-plugin', options, captureSubmittingElement);
882 | };
883 |
884 | // private event handlers
885 | function doAjaxSubmit(e) {
886 | /*jshint validthis:true */
887 | var options = e.data;
888 | if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
889 | e.preventDefault();
890 | $(e.target).ajaxSubmit(options); // #365
891 | }
892 | }
893 |
894 | function captureSubmittingElement(e) {
895 | /*jshint validthis:true */
896 | var target = e.target;
897 | var $el = $(target);
898 | if (!($el.is("[type=submit],[type=image]"))) {
899 | // is this a child element of the submit el? (ex: a span within a button)
900 | var t = $el.closest('[type=submit]');
901 | if (t.length === 0) {
902 | return;
903 | }
904 | target = t[0];
905 | }
906 | var form = this;
907 | form.clk = target;
908 | if (target.type == 'image') {
909 | if (e.offsetX !== undefined) {
910 | form.clk_x = e.offsetX;
911 | form.clk_y = e.offsetY;
912 | } else if (typeof $.fn.offset == 'function') {
913 | var offset = $el.offset();
914 | form.clk_x = e.pageX - offset.left;
915 | form.clk_y = e.pageY - offset.top;
916 | } else {
917 | form.clk_x = e.pageX - target.offsetLeft;
918 | form.clk_y = e.pageY - target.offsetTop;
919 | }
920 | }
921 | // clear form vars
922 | setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
923 | }
924 |
925 |
926 | // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
927 | $.fn.ajaxFormUnbind = function() {
928 | return this.unbind('submit.form-plugin click.form-plugin');
929 | };
930 |
931 | /**
932 | * formToArray() gathers form element data into an array of objects that can
933 | * be passed to any of the following ajax functions: $.get, $.post, or load.
934 | * Each object in the array has both a 'name' and 'value' property. An example of
935 | * an array for a simple login form might be:
936 | *
937 | * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
938 | *
939 | * It is this array that is passed to pre-submit callback functions provided to the
940 | * ajaxSubmit() and ajaxForm() methods.
941 | */
942 | $.fn.formToArray = function(semantic, elements) {
943 | var a = [];
944 | if (this.length === 0) {
945 | return a;
946 | }
947 |
948 | var form = this[0];
949 | var formId = this.attr('id');
950 | var els = semantic ? form.getElementsByTagName('*') : form.elements;
951 | var els2;
952 |
953 | if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
954 | els = $(els).get(); // convert to standard array
955 | }
956 |
957 | // #386; account for inputs outside the form which use the 'form' attribute
958 | if ( formId ) {
959 | els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
960 | if ( els2.length ) {
961 | els = (els || []).concat(els2);
962 | }
963 | }
964 |
965 | if (!els || !els.length) {
966 | return a;
967 | }
968 |
969 | var i,j,n,v,el,max,jmax;
970 | for(i=0, max=els.length; i < max; i++) {
971 | el = els[i];
972 | n = el.name;
973 | if (!n || el.disabled) {
974 | continue;
975 | }
976 |
977 | if (semantic && form.clk && el.type == "image") {
978 | // handle image inputs on the fly when semantic == true
979 | if(form.clk == el) {
980 | a.push({name: n, value: $(el).val(), type: el.type });
981 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
982 | }
983 | continue;
984 | }
985 |
986 | v = $.fieldValue(el, true);
987 | if (v && v.constructor == Array) {
988 | if (elements) {
989 | elements.push(el);
990 | }
991 | for(j=0, jmax=v.length; j < jmax; j++) {
992 | a.push({name: n, value: v[j]});
993 | }
994 | }
995 | else if (feature.fileapi && el.type == 'file') {
996 | if (elements) {
997 | elements.push(el);
998 | }
999 | var files = el.files;
1000 | if (files.length) {
1001 | for (j=0; j < files.length; j++) {
1002 | a.push({name: n, value: files[j], type: el.type});
1003 | }
1004 | }
1005 | else {
1006 | // #180
1007 | a.push({ name: n, value: '', type: el.type });
1008 | }
1009 | }
1010 | else if (v !== null && typeof v != 'undefined') {
1011 | if (elements) {
1012 | elements.push(el);
1013 | }
1014 | a.push({name: n, value: v, type: el.type, required: el.required});
1015 | }
1016 | }
1017 |
1018 | if (!semantic && form.clk) {
1019 | // input type=='image' are not found in elements array! handle it here
1020 | var $input = $(form.clk), input = $input[0];
1021 | n = input.name;
1022 | if (n && !input.disabled && input.type == 'image') {
1023 | a.push({name: n, value: $input.val()});
1024 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
1025 | }
1026 | }
1027 | return a;
1028 | };
1029 |
1030 | /**
1031 | * Serializes form data into a 'submittable' string. This method will return a string
1032 | * in the format: name1=value1&name2=value2
1033 | */
1034 | $.fn.formSerialize = function(semantic) {
1035 | //hand off to jQuery.param for proper encoding
1036 | return $.param(this.formToArray(semantic));
1037 | };
1038 |
1039 | /**
1040 | * Serializes all field elements in the jQuery object into a query string.
1041 | * This method will return a string in the format: name1=value1&name2=value2
1042 | */
1043 | $.fn.fieldSerialize = function(successful) {
1044 | var a = [];
1045 | this.each(function() {
1046 | var n = this.name;
1047 | if (!n) {
1048 | return;
1049 | }
1050 | var v = $.fieldValue(this, successful);
1051 | if (v && v.constructor == Array) {
1052 | for (var i=0,max=v.length; i < max; i++) {
1053 | a.push({name: n, value: v[i]});
1054 | }
1055 | }
1056 | else if (v !== null && typeof v != 'undefined') {
1057 | a.push({name: this.name, value: v});
1058 | }
1059 | });
1060 | //hand off to jQuery.param for proper encoding
1061 | return $.param(a);
1062 | };
1063 |
1064 | /**
1065 | * Returns the value(s) of the element in the matched set. For example, consider the following form:
1066 | *
1067 | *
1075 | *
1076 | * var v = $('input[type=text]').fieldValue();
1077 | * // if no values are entered into the text inputs
1078 | * v == ['','']
1079 | * // if values entered into the text inputs are 'foo' and 'bar'
1080 | * v == ['foo','bar']
1081 | *
1082 | * var v = $('input[type=checkbox]').fieldValue();
1083 | * // if neither checkbox is checked
1084 | * v === undefined
1085 | * // if both checkboxes are checked
1086 | * v == ['B1', 'B2']
1087 | *
1088 | * var v = $('input[type=radio]').fieldValue();
1089 | * // if neither radio is checked
1090 | * v === undefined
1091 | * // if first radio is checked
1092 | * v == ['C1']
1093 | *
1094 | * The successful argument controls whether or not the field element must be 'successful'
1095 | * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1096 | * The default value of the successful argument is true. If this value is false the value(s)
1097 | * for each element is returned.
1098 | *
1099 | * Note: This method *always* returns an array. If no valid value can be determined the
1100 | * array will be empty, otherwise it will contain one or more values.
1101 | */
1102 | $.fn.fieldValue = function(successful) {
1103 | for (var val=[], i=0, max=this.length; i < max; i++) {
1104 | var el = this[i];
1105 | var v = $.fieldValue(el, successful);
1106 | if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1107 | continue;
1108 | }
1109 | if (v.constructor == Array) {
1110 | $.merge(val, v);
1111 | }
1112 | else {
1113 | val.push(v);
1114 | }
1115 | }
1116 | return val;
1117 | };
1118 |
1119 | /**
1120 | * Returns the value of the field element.
1121 | */
1122 | $.fieldValue = function(el, successful) {
1123 | var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1124 | if (successful === undefined) {
1125 | successful = true;
1126 | }
1127 |
1128 | if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
1129 | (t == 'checkbox' || t == 'radio') && !el.checked ||
1130 | (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
1131 | tag == 'select' && el.selectedIndex == -1)) {
1132 | return null;
1133 | }
1134 |
1135 | if (tag == 'select') {
1136 | var index = el.selectedIndex;
1137 | if (index < 0) {
1138 | return null;
1139 | }
1140 | var a = [], ops = el.options;
1141 | var one = (t == 'select-one');
1142 | var max = (one ? index+1 : ops.length);
1143 | for(var i=(one ? index : 0); i < max; i++) {
1144 | var op = ops[i];
1145 | if (op.selected) {
1146 | var v = op.value;
1147 | if (!v) { // extra pain for IE...
1148 | v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
1149 | }
1150 | if (one) {
1151 | return v;
1152 | }
1153 | a.push(v);
1154 | }
1155 | }
1156 | return a;
1157 | }
1158 | return $(el).val();
1159 | };
1160 |
1161 | /**
1162 | * Clears the form data. Takes the following actions on the form's input fields:
1163 | * - input text fields will have their 'value' property set to the empty string
1164 | * - select elements will have their 'selectedIndex' property set to -1
1165 | * - checkbox and radio inputs will have their 'checked' property set to false
1166 | * - inputs of type submit, button, reset, and hidden will *not* be effected
1167 | * - button elements will *not* be effected
1168 | */
1169 | $.fn.clearForm = function(includeHidden) {
1170 | return this.each(function() {
1171 | $('input,select,textarea', this).clearFields(includeHidden);
1172 | });
1173 | };
1174 |
1175 | /**
1176 | * Clears the selected form elements.
1177 | */
1178 | $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1179 | var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1180 | return this.each(function() {
1181 | var t = this.type, tag = this.tagName.toLowerCase();
1182 | if (re.test(t) || tag == 'textarea') {
1183 | this.value = '';
1184 | }
1185 | else if (t == 'checkbox' || t == 'radio') {
1186 | this.checked = false;
1187 | }
1188 | else if (tag == 'select') {
1189 | this.selectedIndex = -1;
1190 | }
1191 | else if (t == "file") {
1192 | if (/MSIE/.test(navigator.userAgent)) {
1193 | $(this).replaceWith($(this).clone(true));
1194 | } else {
1195 | $(this).val('');
1196 | }
1197 | }
1198 | else if (includeHidden) {
1199 | // includeHidden can be the value true, or it can be a selector string
1200 | // indicating a special test; for example:
1201 | // $('#myForm').clearForm('.special:hidden')
1202 | // the above would clean hidden inputs that have the class of 'special'
1203 | if ( (includeHidden === true && /hidden/.test(t)) ||
1204 | (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
1205 | this.value = '';
1206 | }
1207 | }
1208 | });
1209 | };
1210 |
1211 | /**
1212 | * Resets the form data. Causes all form elements to be reset to their original value.
1213 | */
1214 | $.fn.resetForm = function() {
1215 | return this.each(function() {
1216 | // guard against an input with the name of 'reset'
1217 | // note that IE reports the reset function as an 'object'
1218 | if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1219 | this.reset();
1220 | }
1221 | });
1222 | };
1223 |
1224 | /**
1225 | * Enables or disables any matching elements.
1226 | */
1227 | $.fn.enable = function(b) {
1228 | if (b === undefined) {
1229 | b = true;
1230 | }
1231 | return this.each(function() {
1232 | this.disabled = !b;
1233 | });
1234 | };
1235 |
1236 | /**
1237 | * Checks/unchecks any matching checkboxes or radio buttons and
1238 | * selects/deselects and matching option elements.
1239 | */
1240 | $.fn.selected = function(select) {
1241 | if (select === undefined) {
1242 | select = true;
1243 | }
1244 | return this.each(function() {
1245 | var t = this.type;
1246 | if (t == 'checkbox' || t == 'radio') {
1247 | this.checked = select;
1248 | }
1249 | else if (this.tagName.toLowerCase() == 'option') {
1250 | var $sel = $(this).parent('select');
1251 | if (select && $sel[0] && $sel[0].type == 'select-one') {
1252 | // deselect all other options
1253 | $sel.find('option').selected(false);
1254 | }
1255 | this.selected = select;
1256 | }
1257 | });
1258 | };
1259 |
1260 | // expose debug var
1261 | $.fn.ajaxSubmit.debug = false;
1262 |
1263 | // helper fn for console logging
1264 | function log() {
1265 | if (!$.fn.ajaxSubmit.debug) {
1266 | return;
1267 | }
1268 | var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1269 | if (window.console && window.console.log) {
1270 | window.console.log(msg);
1271 | }
1272 | else if (window.opera && window.opera.postError) {
1273 | window.opera.postError(msg);
1274 | }
1275 | }
1276 |
1277 | }));
1278 |
--------------------------------------------------------------------------------