最近接了个活,被狠狠的坑了一把,也没有什么收获。接近尾声,把唯一的一点成果发出来。
新项目,没有完整的前端框架,选择了layui做为后台管理系统的前端js框架。layui挺好的(尤其是layui.use声明式的组件加载,要比提前大量的js文件引入优雅的多),但是毕竟是新的框架,可用的东西太少了,很多封装的组件也比较简单,适应不了后台复杂的展示功能。
新功能有个树表的展示,时间太紧了,常用的treetable做出来也与整个系统的页面风格不统一,无奈只能自己写个treetable的展示页了。
效果图:
主要方法:
openChild(elem,id) 节点打开关闭
addChild(item,tr) 添加节点
addChildCity(id,$(tr).next()) 添加节点后的回调,自动把创建的节点添加到表格中
del(id,$that) 删除节点
整个过程中没有刷新操作,全部jquery实现table的变动。
相对treetable组件,treetable是一行一个table组织起来的,这个treetable是整个一个表格。
代码还没有整理封装,可以快速替换自己的样式和model。
layui的所有代码都封装layui.use中,外部不可见的,如果想外部调用layui的方法,可以先声明,在layui中再定义方法实现。例如:
var addChildCity = null; var del = null; 两个方法。
还有比较坑的是layui.js加载组件路径的声明,开始使用的时候就是不能准确加载组件,无奈写死了请求路径方才可以。
var p = n.createElement("script"), h = (a[m] ? "/static/plugins/layui/lay/" : o.base(layui.js 76行)
具体原因未找到,可能是自己使用的不太对吧。
还是要声明一下,如果使用layui,一定要确定自己有封装组件的能力和充足的时间,否则还是用功能完整的extjs吧,急就章赶出来的代码,实在不敢恭维。
过两天封装组件代码.......................................................................................
添加根节点
名称
简称
全拼
简拼
编码
是否主要
映射编码
序号
级别
操作
再附上一个layui可以使用的jquery-form组件(网上很多如何把jquery组件转成layui的说明,这个是转成功的):
/*
* ! jQuery Form Plugin version: 3.50.0-2014.02.05 Requires jQuery v1.5 or later
* Copyright (c) 2013 M. Alsup Examples and documentation at:
* http://malsup.com/jquery/form/ Project repository:
* https://github.com/malsup/form Dual licensed under the MIT and GPL licenses.
* https://github.com/malsup/form#copyright-and-license
*/
/* global ActiveXObject */
// AMD support
layui.define(function(exports) {
function jquery_form(jQuery) {
// 插件代码区
(function(factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
// using AMD; register as anon module
define(['jquery'], factory);
} else {
// no AMD; invoke directly
factory((typeof(jQuery) != 'undefined') ? jQuery : window.Zepto);
}
}
(function($) {
"use strict";
/*
* Usage Note: ----------- Do not use both ajaxSubmit and ajaxForm
* on the same form. These functions are mutually exclusive. Use
* ajaxSubmit if you want to bind your own submit handler to the
* form. For example,
*
* $(document).ready(function() { $('#myForm').on('submit',
* function(e) { e.preventDefault(); // <-- important
* $(this).ajaxSubmit({ target: '#output' }); }); });
*
* Use ajaxForm when you want the plugin to manage all the event
* binding for you. For example,
*
* $(document).ready(function() { $('#myForm').ajaxForm({ target:
* '#output' }); });
*
* You can also use ajaxForm with delegation (requires jQuery
* v1.7+), so the form does not have to exist when you invoke
* ajaxForm:
*
* $('#myForm').ajaxForm({ delegation: true, target: '#output' });
*
* When using ajaxForm, the ajaxSubmit function will be invoked for
* you at the appropriate time.
*/
/**
* Feature detection
*/
var feature = {};
feature.fileapi = $("").get(0).files !== undefined;
feature.formdata = window.FormData !== undefined;
var hasProp = !!$.fn.prop;
// attr2 uses prop when it can but checks the return type for
// an expected string. this accounts for the case where a form
// contains inputs with names like "action" or "method"; in those
// cases "prop" returns the element
$.fn.attr2 = function() {
if (!hasProp) {
return this.attr.apply(this, arguments);
}
var val = this.prop.apply(this, arguments);
if ((val && val.jquery) || typeof val === 'string') {
return val;
}
return this.attr.apply(this, arguments);
};
/**
* ajaxSubmit() provides a mechanism for immediately submitting an
* HTML form using AJAX.
*/
$.fn.ajaxSubmit = function(options) {
/* jshint scripturl:true */
// fast fail if nothing selected
// (http://dev.jquery.com/ticket/2752)
if (!this.length) {
log('ajaxSubmit: skipping submit process - no element selected');
return this;
}
var method, action, url, $form = this;
if (typeof options == 'function') {
options = {
success : options
};
} else if (options === undefined) {
options = {};
}
method = options.type || this.attr2('method');
action = options.url || this.attr2('action');
url = (typeof action === 'string') ? $.trim(action) : '';
url = url || window.location.href || '';
if (url) {
// clean url (don't include hash vaue)
url = (url.match(/^([^#]+)/) || [])[1];
}
options = $.extend(true, {
url : url,
success : $.ajaxSettings.success,
type : method || $.ajaxSettings.type,
iframeSrc : /^https/i.test(window.location.href
|| '') ? 'javascript:false' : 'about:blank'
}, options);
// hook for manipulating the form data before it is extracted;
// convenient for use with rich editors like tinyMCE or
// FCKEditor
var veto = {};
this.trigger('form-pre-serialize', [this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
return this;
}
// provide opportunity to alter form data before it is
// serialized
if (options.beforeSerialize
&& options.beforeSerialize(this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSerialize callback');
return this;
}
var traditional = options.traditional;
if (traditional === undefined) {
traditional = $.ajaxSettings.traditional;
}
var elements = [];
var qx, a = this.formToArray(options.semantic, elements);
if (options.data) {
options.extraData = options.data;
qx = $.param(options.data, traditional);
}
// give pre-submit callback an opportunity to abort the submit
if (options.beforeSubmit
&& options.beforeSubmit(a, this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSubmit callback');
return this;
}
// fire vetoable 'validate' event
this.trigger('form-submit-validate', [a, this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
return this;
}
var q = $.param(a, traditional);
if (qx) {
q = (q ? (q + '&' + qx) : qx);
}
if (options.type.toUpperCase() == 'GET') {
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?')
+ q;
options.data = null; // data is null for 'get'
} else {
options.data = q; // data is the query string for 'post'
}
var callbacks = [];
if (options.resetForm) {
callbacks.push(function() {
$form.resetForm();
});
}
if (options.clearForm) {
callbacks.push(function() {
$form.clearForm(options.includeHidden);
});
}
// perform a load on the target only if dataType is not provided
if (!options.dataType && options.target) {
var oldSuccess = options.success || function() {
};
callbacks.push(function(data) {
var fn = options.replaceTarget ? 'replaceWith' : 'html';
$(options.target)[fn](data).each(oldSuccess, arguments);
});
} else if (options.success) {
callbacks.push(options.success);
}
options.success = function(data, status, xhr) { // jQuery 1.4+
// passes xhr as
// 3rd arg
var context = options.context || this; // jQuery 1.4+
// supports scope
// context
for (var i = 0, max = callbacks.length; i < max; i++) {
callbacks[i].apply(context, [data, status,
xhr || $form, $form]);
}
};
if (options.error) {
var oldError = options.error;
options.error = function(xhr, status, error) {
var context = options.context || this;
oldError.apply(context, [xhr, status, error, $form]);
};
}
if (options.complete) {
var oldComplete = options.complete;
options.complete = function(xhr, status) {
var context = options.context || this;
oldComplete.apply(context, [xhr, status, $form]);
};
}
// are there files to upload?
// [value] (issue #113), also see comment:
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
var fileInputs = $('input[type=file]:enabled', this).filter(
function() {
return $(this).val() !== '';
});
var hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form
.attr('encoding') == mp);
var fileAPI = feature.fileapi && feature.formdata;
log("fileAPI :" + fileAPI);
var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
var jqxhr;
// options.iframe allows user to force iframe mode
// 06-NOV-09: now defaulting to iframe mode if file input is
// detected
if (options.iframe !== false
&& (options.iframe || shouldUseFrame)) {
// hack to fix Safari hang (thanks to Tim Molendijk for
// this)
// see:
// http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
if (options.closeKeepAlive) {
$.get(options.closeKeepAlive, function() {
jqxhr = fileUploadIframe(a);
});
} else {
jqxhr = fileUploadIframe(a);
}
} else if ((hasFileInputs || multipart) && fileAPI) {
jqxhr = fileUploadXhr(a);
} else {
jqxhr = $.ajax(options);
}
$form.removeData('jqxhr').data('jqxhr', jqxhr);
// clear element array
for (var k = 0; k < elements.length; k++) {
elements[k] = null;
}
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
return this;
// utility fn for deep serialization
function deepSerialize(extraData) {
var serialized = $.param(extraData, options.traditional)
.split('&');
var len = serialized.length;
var result = [];
var i, part;
for (i = 0; i < len; i++) {
// #252; undo param space replacement
serialized[i] = serialized[i].replace(/\+/g, ' ');
part = serialized[i].split('=');
// #278; use array instead of object storage, favoring
// array serializations
result.push([decodeURIComponent(part[0]),
decodeURIComponent(part[1])]);
}
return result;
}
// XMLHttpRequest Level 2 file uploads (big hat tip to
// francois2metz)
function fileUploadXhr(a) {
var formdata = new FormData();
for (var i = 0; i < a.length; i++) {
formdata.append(a[i].name, a[i].value);
}
if (options.extraData) {
var serializedData = deepSerialize(options.extraData);
for (i = 0; i < serializedData.length; i++) {
if (serializedData[i]) {
formdata.append(serializedData[i][0],
serializedData[i][1]);
}
}
}
options.data = null;
var s = $.extend(true, {}, $.ajaxSettings, options, {
contentType : false,
processData : false,
cache : false,
type : method || 'POST'
});
if (options.uploadProgress) {
// workaround because jqXHR does not expose upload
// property
s.xhr = function() {
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress',
function(event) {
var percent = 0;
var position = event.loaded
|| event.position; /*
* event.position
* is
* deprecated
*/
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position
/ total * 100);
}
options.uploadProgress(event,
position, total, percent);
}, false);
}
return xhr;
};
}
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
// Send FormData() provided by user
if (options.formData) {
o.data = options.formData;
} else {
o.data = formdata;
}
if (beforeSend) {
beforeSend.call(this, xhr, o);
}
};
return $.ajax(s);
}
// private function for handling file uploads (hat tip to
// YAHOO!)
function fileUploadIframe(a) {
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
var deferred = $.Deferred();
// #341
deferred.abort = function(status) {
xhr.abort(status);
};
if (a) {
// ensure that every serialized input is still enabled
for (i = 0; i < elements.length; i++) {
el = $(elements[i]);
if (hasProp) {
el.prop('disabled', false);
} else {
el.removeAttr('disabled');
}
}
}
s = $.extend(true, {}, $.ajaxSettings, options);
s.context = s.context || s;
id = 'jqFormIO' + (new Date().getTime());
if (s.iframeTarget) {
$io = $(s.iframeTarget);
n = $io.attr2('name');
if (!n) {
$io.attr2('name', id);
} else {
id = n;
}
} else {
$io = $('');
$io.css({
position : 'absolute',
top : '-1000px',
left : '-1000px'
});
}
io = $io[0];
xhr = { // mock object
aborted : 0,
responseText : null,
responseXML : null,
status : 0,
statusText : 'n/a',
getAllResponseHeaders : function() {
},
getResponseHeader : function() {
},
setRequestHeader : function() {
},
abort : function(status) {
var e = (status === 'timeout'
? 'timeout'
: 'aborted');
log('aborting upload... ' + e);
this.aborted = 1;
try { // #214, #257
if (io.contentWindow.document.execCommand) {
io.contentWindow.document
.execCommand('Stop');
}
} catch (ignore) {
}
$io.attr('src', s.iframeSrc); // abort op in
// progress
xhr.error = e;
if (s.error) {
s.error.call(s.context, xhr, e, status);
}
if (g) {
$.event.trigger("ajaxError", [xhr, s, e]);
}
if (s.complete) {
s.complete.call(s.context, xhr, e);
}
}
};
g = s.global;
// trigger ajax global events so that activity/block
// indicators work like normal
if (g && 0 === $.active++) {
$.event.trigger("ajaxStart");
}
if (g) {
$.event.trigger("ajaxSend", [xhr, s]);
}
if (s.beforeSend
&& s.beforeSend.call(s.context, xhr, s) === false) {
if (s.global) {
$.active--;
}
deferred.reject();
return deferred;
}
if (xhr.aborted) {
deferred.reject();
return deferred;
}
// add submitting element to data if we know it
sub = form.clk;
if (sub) {
n = sub.name;
if (n && !sub.disabled) {
s.extraData = s.extraData || {};
s.extraData[n] = sub.value;
if (sub.type == "image") {
s.extraData[n + '.x'] = form.clk_x;
s.extraData[n + '.y'] = form.clk_y;
}
}
}
var CLIENT_TIMEOUT_ABORT = 1;
var SERVER_ABORT = 2;
function getDoc(frame) {
/*
* it looks like contentWindow or contentDocument do not
* carry the protocol property in ie8, when running
* under ssl frame.document is the only valid response
* document, since the protocol is know but not on the
* other two objects. strange? "Same origin policy"
* http://en.wikipedia.org/wiki/Same_origin_policy
*/
var doc = null;
// IE8 cascading access check
try {
if (frame.contentWindow) {
doc = frame.contentWindow.document;
}
} catch (err) {
// IE8 access denied under ssl & missing protocol
log('cannot get iframe.contentWindow document: '
+ err);
}
if (doc) { // successful getting content
return doc;
}
try { // simply checking may throw in ie8 under ssl or
// mismatched protocol
doc = frame.contentDocument
? frame.contentDocument
: frame.document;
} catch (err) {
// last attempt
log('cannot get iframe.contentDocument: ' + err);
doc = frame.document;
}
return doc;
}
// Rails CSRF hack (thanks to Yvan Barthelemy)
var csrf_token = $('meta[name=csrf-token]').attr('content');
var csrf_param = $('meta[name=csrf-param]').attr('content');
if (csrf_param && csrf_token) {
s.extraData = s.extraData || {};
s.extraData[csrf_param] = csrf_token;
}
// take a breath so that pending repaints get some cpu time
// before the upload starts
function doSubmit() {
// make sure form attrs are set
var t = $form.attr2('target'), a = $form
.attr2('action'), mp = 'multipart/form-data', et = $form
.attr('enctype')
|| $form.attr('encoding') || mp;
// update form attrs in IE friendly way
form.setAttribute('target', id);
if (!method || /post/i.test(method)) {
form.setAttribute('method', 'POST');
}
if (a != s.url) {
form.setAttribute('action', s.url);
}
// ie borks in some cases when setting encoding
if (!s.skipEncodingOverride
&& (!method || /post/i.test(method))) {
$form.attr({
encoding : 'multipart/form-data',
enctype : 'multipart/form-data'
});
}
// support timout
if (s.timeout) {
timeoutHandle = setTimeout(function() {
timedOut = true;
cb(CLIENT_TIMEOUT_ABORT);
}, s.timeout);
}
// look for server aborts
function checkState() {
try {
var state = getDoc(io).readyState;
log('state = ' + state);
if (state
&& state.toLowerCase() == 'uninitialized') {
setTimeout(checkState, 50);
}
} catch (e) {
log('Server abort: ', e, ' (', e.name, ')');
cb(SERVER_ABORT);
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
timeoutHandle = undefined;
}
}
// add "extra" data to form if provided in options
var extraInputs = [];
try {
if (s.extraData) {
for (var n in s.extraData) {
if (s.extraData.hasOwnProperty(n)) {
// if using the $.param format that
// allows for multiple values with the
// same name
if ($.isPlainObject(s.extraData[n])
&& s.extraData[n]
.hasOwnProperty('name')
&& s.extraData[n]
.hasOwnProperty('value')) {
extraInputs
.push( $('')
.val(s.extraData[n].value)
.appendTo(form)[0]);
} else {
extraInputs
.push( $('')
.val(s.extraData[n])
.appendTo(form)[0]);
}
}
}
}
if (!s.iframeTarget) {
// add iframe to doc and submit the form
$io.appendTo('body');
}
if (io.attachEvent) {
io.attachEvent('onload', cb);
} else {
io.addEventListener('load', cb, false);
}
setTimeout(checkState, 15);
try {
form.submit();
} catch (err) {
// just in case form has element with name/id of
// 'submit'
var submitFn = document.createElement('form').submit;
submitFn.apply(form);
}
} finally {
// reset attrs and remove "extra" input elements
form.setAttribute('action', a);
form.setAttribute('enctype', et); // #380
if (t) {
form.setAttribute('target', t);
} else {
$form.removeAttr('target');
}
$(extraInputs).remove();
}
}
if (s.forceSync) {
doSubmit();
} else {
setTimeout(doSubmit, 10); // this lets dom updates
// render
}
var data, doc, domCheckCount = 50, callbackProcessed;
function cb(e) {
if (xhr.aborted || callbackProcessed) {
return;
}
doc = getDoc(io);
if (!doc) {
log('cannot access response document');
e = SERVER_ABORT;
}
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
xhr.abort('timeout');
deferred.reject(xhr, 'timeout');
return;
} else if (e == SERVER_ABORT && xhr) {
xhr.abort('server abort');
deferred.reject(xhr, 'error', 'server abort');
return;
}
if (!doc || doc.location.href == s.iframeSrc) {
// response not received yet
if (!timedOut) {
return;
}
}
if (io.detachEvent) {
io.detachEvent('onload', cb);
} else {
io.removeEventListener('load', cb, false);
}
var status = 'success', errMsg;
try {
if (timedOut) {
throw 'timeout';
}
var isXml = s.dataType == 'xml' || doc.XMLDocument
|| $.isXMLDoc(doc);
log('isXml=' + isXml);
if (!isXml
&& window.opera
&& (doc.body === null || !doc.body.innerHTML)) {
if (--domCheckCount) {
// in some browsers (Opera) the iframe DOM
// is not always traversable when
// the onload callback fires, so we loop a
// bit to accommodate
log('requeing onLoad callback, DOM not available');
setTimeout(cb, 250);
return;
}
// let this fall through because server response
// could be an empty document
// log('Could not access iframe DOM after
// mutiple tries.');
// throw 'DOMException: not available';
}
// log('response detected');
var docRoot = doc.body
? doc.body
: doc.documentElement;
xhr.responseText = docRoot
? docRoot.innerHTML
: null;
xhr.responseXML = doc.XMLDocument
? doc.XMLDocument
: doc;
if (isXml) {
s.dataType = 'xml';
}
xhr.getResponseHeader = function(header) {
var headers = {
'content-type' : s.dataType
};
return headers[header.toLowerCase()];
};
// support for XHR 'status' & 'statusText' emulation
// :
if (docRoot) {
xhr.status = Number(docRoot
.getAttribute('status'))
|| xhr.status;
xhr.statusText = docRoot
.getAttribute('statusText')
|| xhr.statusText;
}
var dt = (s.dataType || '').toLowerCase();
var scr = /(json|script|text)/.test(dt);
if (scr || s.textarea) {
// see if user embedded response in textarea
var ta = doc.getElementsByTagName('textarea')[0];
if (ta) {
xhr.responseText = ta.value;
// support for XHR 'status' & 'statusText'
// emulation :
xhr.status = Number(ta
.getAttribute('status'))
|| xhr.status;
xhr.statusText = ta
.getAttribute('statusText')
|| xhr.statusText;
} else if (scr) {
// account for browsers injecting pre around
// json response
var pre = doc.getElementsByTagName('pre')[0];
var b = doc.getElementsByTagName('body')[0];
if (pre) {
xhr.responseText = pre.textContent
? pre.textContent
: pre.innerText;
} else if (b) {
xhr.responseText = b.textContent
? b.textContent
: b.innerText;
}
}
} else if (dt == 'xml' && !xhr.responseXML
&& xhr.responseText) {
xhr.responseXML = toXml(xhr.responseText);
}
try {
data = httpData(xhr, dt, s);
} catch (err) {
status = 'parsererror';
xhr.error = errMsg = (err || status);
}
} catch (err) {
log('error caught: ', err);
status = 'error';
xhr.error = errMsg = (err || status);
}
if (xhr.aborted) {
log('upload aborted');
status = null;
}
if (xhr.status) { // we've set xhr.status
status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)
? 'success'
: 'error';
}
// ordering of these callbacks/triggers is odd, but
// that's how $.ajax does it
if (status === 'success') {
if (s.success) {
s.success.call(s.context, data, 'success', xhr);
}
deferred.resolve(xhr.responseText, 'success', xhr);
if (g) {
$.event.trigger("ajaxSuccess", [xhr, s]);
}
} else if (status) {
if (errMsg === undefined) {
errMsg = xhr.statusText;
}
if (s.error) {
s.error.call(s.context, xhr, status, errMsg);
}
deferred.reject(xhr, 'error', errMsg);
if (g) {
$.event.trigger("ajaxError", [xhr, s, errMsg]);
}
}
if (g) {
$.event.trigger("ajaxComplete", [xhr, s]);
}
if (g && !--$.active) {
$.event.trigger("ajaxStop");
}
if (s.complete) {
s.complete.call(s.context, xhr, status);
}
callbackProcessed = true;
if (s.timeout) {
clearTimeout(timeoutHandle);
}
// clean up
setTimeout(function() {
if (!s.iframeTarget) {
$io.remove();
} else { // adding else to clean up
// existing iframe response.
$io.attr('src', s.iframeSrc);
}
xhr.responseXML = null;
}, 100);
}
var toXml = $.parseXML || function(s, doc) { // use
// parseXML
// if
// available
// (jQuery
// 1.5+)
if (window.ActiveXObject) {
doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = 'false';
doc.loadXML(s);
} else {
doc = (new DOMParser()).parseFromString(s,
'text/xml');
}
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror')
? doc
: null;
};
var parseJSON = $.parseJSON || function(s) {
/* jslint evil:true */
return window['eval']('(' + s + ')');
};
var httpData = function(xhr, type, s) { // mostly lifted
// from jq1.4.4
var ct = xhr.getResponseHeader('content-type') || '', xml = type === 'xml'
|| !type && ct.indexOf('xml') >= 0, data = xml
? xhr.responseXML
: xhr.responseText;
if (xml
&& data.documentElement.nodeName === 'parsererror') {
if ($.error) {
$.error('parsererror');
}
}
if (s && s.dataFilter) {
data = s.dataFilter(data, type);
}
if (typeof data === 'string') {
if (type === 'json' || !type
&& ct.indexOf('json') >= 0) {
data = parseJSON(data);
} else if (type === "script" || !type
&& ct.indexOf("javascript") >= 0) {
$.globalEval(data);
}
}
return data;
};
return deferred;
}
};
/**
* ajaxForm() provides a mechanism for fully automating form
* submission.
*
* The advantages of using this method instead of ajaxSubmit() are:
*
* 1: This method will include coordinates for
* elements (if the element is used to submit the form). 2. This
* method will include the submit element's name/value data (for the
* element that was used to submit the form). 3. This method binds
* the submit() method to the form for you.
*
* The options argument for ajaxForm works exactly as it does for
* ajaxSubmit. ajaxForm merely passes the options argument along
* after properly binding events for submit elements and the form
* itself.
*/
$.fn.ajaxForm = function(options) {
options = options || {};
options.delegation = options.delegation
&& $.isFunction($.fn.on);
// in jQuery 1.3+ we can fix mistakes with the ready state
if (!options.delegation && this.length === 0) {
var o = {
s : this.selector,
c : this.context
};
if (!$.isReady && o.s) {
log('DOM not ready, queuing ajaxForm');
$(function() {
$(o.s, o.c).ajaxForm(options);
});
return this;
}
// is your DOM ready?
// http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
log('terminating; zero elements found by selector'
+ ($.isReady ? '' : ' (DOM not ready)'));
return this;
}
if (options.delegation) {
$(document).off('submit.form-plugin', this.selector,
doAjaxSubmit).off('click.form-plugin',
this.selector, captureSubmittingElement).on(
'submit.form-plugin', this.selector, options,
doAjaxSubmit).on('click.form-plugin',
this.selector, options, captureSubmittingElement);
return this;
}
return this.ajaxFormUnbind().bind('submit.form-plugin',
options, doAjaxSubmit).bind('click.form-plugin',
options, captureSubmittingElement);
};
// private event handlers
function doAjaxSubmit(e) {
/* jshint validthis:true */
var options = e.data;
if (!e.isDefaultPrevented()) { // if event has been canceled,
// don't proceed
e.preventDefault();
$(e.target).ajaxSubmit(options); // #365
}
}
function captureSubmittingElement(e) {
/* jshint validthis:true */
var target = e.target;
var $el = $(target);
if (!($el.is("[type=submit],[type=image]"))) {
// is this a child element of the submit el? (ex: a span
// within a button)
var t = $el.closest('[type=submit]');
if (t.length === 0) {
return;
}
target = t[0];
}
var form = this;
form.clk = target;
if (target.type == 'image') {
if (e.offsetX !== undefined) {
form.clk_x = e.offsetX;
form.clk_y = e.offsetY;
} else if (typeof $.fn.offset == 'function') {
var offset = $el.offset();
form.clk_x = e.pageX - offset.left;
form.clk_y = e.pageY - offset.top;
} else {
form.clk_x = e.pageX - target.offsetLeft;
form.clk_y = e.pageY - target.offsetTop;
}
}
// clear form vars
setTimeout(function() {
form.clk = form.clk_x = form.clk_y = null;
}, 100);
}
// ajaxFormUnbind unbinds the event handlers that were bound by
// ajaxForm
$.fn.ajaxFormUnbind = function() {
return this.unbind('submit.form-plugin click.form-plugin');
};
/**
* formToArray() gathers form element data into an array of objects
* that can be passed to any of the following ajax functions: $.get,
* $.post, or load. Each object in the array has both a 'name' and
* 'value' property. An example of an array for a simple login form
* might be:
* [ { name: 'username', value: 'jresig' }, { name: 'password',
* value: 'secret' } ]
*
* It is this array that is passed to pre-submit callback functions
* provided to the ajaxSubmit() and ajaxForm() methods.
*/
$.fn.formToArray = function(semantic, elements) {
var a = [];
if (this.length === 0) {
return a;
}
var form = this[0];
var formId = this.attr('id');
var els = semantic
? form.getElementsByTagName('*')
: form.elements;
var els2;
if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
els = $(els).get(); // convert to standard array
}
// #386; account for inputs outside the form which use the
// 'form' attribute
if (formId) {
els2 = $(':input[form=' + formId + ']').get();
if (els2.length) {
els = (els || []).concat(els2);
}
}
if (!els || !els.length) {
return a;
}
var i, j, n, v, el, max, jmax;
for (i = 0, max = els.length; i < max; i++) {
el = els[i];
n = el.name;
if (!n || el.disabled) {
continue;
}
if (semantic && form.clk && el.type == "image") {
// handle image inputs on the fly when semantic == true
if (form.clk == el) {
a.push({
name : n,
value : $(el).val(),
type : el.type
});
a.push({
name : n + '.x',
value : form.clk_x
}, {
name : n + '.y',
value : form.clk_y
});
}
continue;
}
v = $.fieldValue(el, true);
if (v && v.constructor == Array) {
if (elements) {
elements.push(el);
}
for (j = 0, jmax = v.length; j < jmax; j++) {
a.push({
name : n,
value : v[j]
});
}
} else if (feature.fileapi && el.type == 'file') {
if (elements) {
elements.push(el);
}
var files = el.files;
if (files.length) {
for (j = 0; j < files.length; j++) {
a.push({
name : n,
value : files[j],
type : el.type
});
}
} else {
// #180
a.push({
name : n,
value : '',
type : el.type
});
}
} else if (v !== null && typeof v != 'undefined') {
if (elements) {
elements.push(el);
}
a.push({
name : n,
value : v,
type : el.type,
required : el.required
});
}
}
if (!semantic && form.clk) {
// input type=='image' are not found in elements array!
// handle it here
var $input = $(form.clk), input = $input[0];
n = input.name;
if (n && !input.disabled && input.type == 'image') {
a.push({
name : n,
value : $input.val()
});
a.push({
name : n + '.x',
value : form.clk_x
}, {
name : n + '.y',
value : form.clk_y
});
}
}
return a;
};
/**
* Serializes form data into a 'submittable' string. This method
* will return a string in the format: name1=value1&name2=value2
*/
$.fn.formSerialize = function(semantic) {
// hand off to jQuery.param for proper encoding
return $.param(this.formToArray(semantic));
};
/**
* Serializes all field elements in the jQuery object into a query
* string. This method will return a string in the format:
* name1=value1&name2=value2
*/
$.fn.fieldSerialize = function(successful) {
var a = [];
this.each(function() {
var n = this.name;
if (!n) {
return;
}
var v = $.fieldValue(this, successful);
if (v && v.constructor == Array) {
for (var i = 0, max = v.length; i < max; i++) {
a.push({
name : n,
value : v[i]
});
}
} else if (v !== null && typeof v != 'undefined') {
a.push({
name : this.name,
value : v
});
}
});
// hand off to jQuery.param for proper encoding
return $.param(a);
};
/**
* Returns the value(s) of the element in the matched set. For
* example, consider the following form:
*
*
*
* var v = $('input[type=text]').fieldValue(); // if no values are
* entered into the text inputs v == ['',''] // if values entered
* into the text inputs are 'foo' and 'bar' v == ['foo','bar']
*
* var v = $('input[type=checkbox]').fieldValue(); // if neither
* checkbox is checked v === undefined // if both checkboxes are
* checked v == ['B1', 'B2']
*
* var v = $('input[type=radio]').fieldValue(); // if neither radio
* is checked v === undefined // if first radio is checked v ==
* ['C1']
*
* The successful argument controls whether or not the field element
* must be 'successful' (per
* http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
* The default value of the successful argument is true. If this
* value is false the value(s) for each element is returned.
*
* Note: This method *always* returns an array. If no valid value
* can be determined the array will be empty, otherwise it will
* contain one or more values.
*/
$.fn.fieldValue = function(successful) {
for (var val = [], i = 0, max = this.length; i < max; i++) {
var el = this[i];
var v = $.fieldValue(el, successful);
if (v === null || typeof v == 'undefined'
|| (v.constructor == Array && !v.length)) {
continue;
}
if (v.constructor == Array) {
$.merge(val, v);
} else {
val.push(v);
}
}
return val;
};
/**
* Returns the value of the field element.
*/
$.fieldValue = function(el, successful) {
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
if (successful === undefined) {
successful = true;
}
if (successful
&& (!n || el.disabled || t == 'reset' || t == 'button'
|| (t == 'checkbox' || t == 'radio')
&& !el.checked
|| (t == 'submit' || t == 'image') && el.form
&& el.form.clk != el || tag == 'select'
&& el.selectedIndex == -1)) {
return null;
}
if (tag == 'select') {
var index = el.selectedIndex;
if (index < 0) {
return null;
}
var a = [], ops = el.options;
var one = (t == 'select-one');
var max = (one ? index + 1 : ops.length);
for (var i = (one ? index : 0); i < max; i++) {
var op = ops[i];
if (op.selected) {
var v = op.value;
if (!v) { // extra pain for IE...
v = (op.attributes && op.attributes.value && !(op.attributes.value.specified))
? op.text
: op.value;
}
if (one) {
return v;
}
a.push(v);
}
}
return a;
}
return $(el).val();
};
/**
* Clears the form data. Takes the following actions on the form's
* input fields: - input text fields will have their 'value'
* property set to the empty string - select elements will have
* their 'selectedIndex' property set to -1 - checkbox and radio
* inputs will have their 'checked' property set to false - inputs
* of type submit, button, reset, and hidden will *not* be effected -
* button elements will *not* be effected
*/
$.fn.clearForm = function(includeHidden) {
return this.each(function() {
$('input,select,textarea', this).clearFields(includeHidden);
});
};
/**
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden'
// is
// not
// in
// this
// list
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
if (re.test(t) || tag == 'textarea') {
this.value = '';
} else if (t == 'checkbox' || t == 'radio') {
this.checked = false;
} else if (tag == 'select') {
this.selectedIndex = -1;
} else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true));
} else {
$(this).val('');
}
} else if (includeHidden) {
// includeHidden can be the value true, or it
// can be a selector string
// indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have
// the class of 'special'
if ((includeHidden === true && /hidden/.test(t))
|| (typeof includeHidden == 'string' && $(this)
.is(includeHidden))) {
this.value = '';
}
}
});
};
/**
* Resets the form data. Causes all form elements to be reset to
* their original value.
*/
$.fn.resetForm = function() {
return this.each(function() {
// guard against an input with the name of 'reset'
// note that IE reports the reset function as an 'object'
if (typeof this.reset == 'function'
|| (typeof this.reset == 'object' && !this.reset.nodeType)) {
this.reset();
}
});
};
/**
* Enables or disables any matching elements.
*/
$.fn.enable = function(b) {
if (b === undefined) {
b = true;
}
return this.each(function() {
this.disabled = !b;
});
};
/**
* Checks/unchecks any matching checkboxes or radio buttons and
* selects/deselects and matching option elements.
*/
$.fn.selected = function(select) {
if (select === undefined) {
select = true;
}
return this.each(function() {
var t = this.type;
if (t == 'checkbox' || t == 'radio') {
this.checked = select;
} else if (this.tagName.toLowerCase() == 'option') {
var $sel = $(this).parent('select');
if (select && $sel[0]
&& $sel[0].type == 'select-one') {
// deselect all other options
$sel.find('option').selected(false);
}
this.selected = select;
}
});
};
// expose debug var
$.fn.ajaxSubmit.debug = false;
// helper fn for console logging
function log() {
if (!$.fn.ajaxSubmit.debug) {
return;
}
var msg = '[jquery.form] '
+ Array.prototype.join.call(arguments, '');
if (window.console && window.console.log) {
window.console.log(msg);
} else if (window.opera && window.opera.postError) {
window.opera.postError(msg);
}
}
}));
return jQuery;
};
exports('jquery_form', jquery_form);
});