JQuery是目前在JavaScript领域上最广泛流行的框架,无奈这次把他加到 ECShop的时候就遇到一个十分头疼的问题。
ECShop把AJAX事件和JSON解析的模块放在common/transport.js之中,可以说它也有自己封装的一套工具,这其实是很正常的。
但恰恰的,在封装JSON各种方法的同时对object的模型进行了重写,这个就跟jQuery冲突了。因为众所周知的,jQuery对各种 JavaScript对象进行了扩展。
我在开发康比特网站的时候也遇到了这个问题,需要用放大镜效果、仿淘宝的物流追踪等功能时,都会发生冲突。在ECShop论坛上原来也有很多朋友提出了这个问题,也提出了各种各样的方法,我尝试了一些,不好或者甚至无用,所以只好自己动手了。
解决思路:
一切起因就是在于两者都重写了Object,所以当然任意一方让步就可以了。在这里我选择了修改代码量比较少的transport.js,除了简单,更重要的是去修改jQuery的话框架提供的强大特效和各种敏捷方法就失效了,还不是得不偿失。
1、加入JSON2.js文件
原因很简单,transport修改Object是为了加入支持JSON的方法。所以我这里用JSON官网在javascript语言上提供的json2.js支持具体下载地址 http://www.json.org/json2.js,从官方下载下来的json2.js 第一句话需要注释:alert(‘IMPORTANT: Remove this line from json2.js beforedeployment.’); 否则的话 加载json2.js页面 就会弹出
2、修改transport.js
2.1 注释掉重写object的方法,具体搜索
Js代码
- if ( ! Object.prototype.toJSONString) {
2.2 加入新的JSON支持方法
Js代码
- function objToJSONString(obj, filter){
- return JSON.stringify(obj, filter);
- }
- function parseObjectToJSON(object, filter){
- return JSON.parse(object, filter);
- }
也可以看到其实都很简单,都是调用json2.js里面提供的方法,有人觉得不必在这里添加直接调用也可以,但我还是建议做这样的一个适配器在这,因为容易容易表明用意。
2.3 修正AJAX方法
2.3.1 在transport.js搜索params.toJSONString() 改为 objToJSONString(params)
2.3.2 继续在transport.js搜索result.parseJSON() 改为 parseObjectToJSON(result)(具体在408行左右)
3、修复旧JSON调用地方
简单来说就是把所有*.toJSONString() 的调用改为用objToJSONString(*),而*.parseJSON()改为parseObjectToJSON(*)。
例如: commom.js里面第一个函数里面的
- Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), addToCartResponse, 'POST', 'JSON');
改为:
- Ajax.call('flow.php?step=add_to_cart', 'goods=' + objToJSONString(goods), addToCartResponse, 'POST', 'JSON');
commom.js里面还有一些Ajax.call调用toJSONString的,都需要按照本例替换。
我个人建议全部替换,如果当时不替换 如果再次二次开发时 又发生冲突 还得查资料 还不如一下搞定 ,js文件也不多 我自己修改时用了10多分钟就搞定了,建议在header里面就加入了json2.js
最好的解决方案就坐等ECShop来搞定了。
同样的,这次的冲突不具备特殊性,也是一次很好的经验,因为以后遇到修改object等对象的场合并不会少,有这次的经验就知道如何应对了。
PS:附件为修复后的transport.js文件,注意调用之前必须加入json2.js文件。
附加:transport.js修改版代码
- /**
- * @file transport.js
- * @description 用于支持AJAX的传输类。
- * @author ECShop R&D Team ( http://www.ecshop.com/ )
- * @date 2007-03-08 Wednesday
- * @license Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
- * @version 1.0.20070308
- **/
- var Transport =
- {
- /* *
- * 存储本对象所在的文件名。
- *
- * @static
- */
- filename : "transport.js",
- /* *
- * 存储是否进入调试模式的开关,打印调试消息的方式,换行符,调试用的容器的ID。
- *
- * @private
- */
- debugging :
- {
- isDebugging : 0,
- debuggingMode : 0,
- linefeed : "",
- containerId : 0
- },
- /* *
- * 设置调试模式以及打印调试消息方式的方法。
- *
- * @public
- * @param {int} 是否打开调试模式 0:关闭,1:打开
- * @param {int} 打印调试消息的方式 0:alert,1:innerHTML
- *
- */
- debug : function (isDebugging, debuggingMode)
- {
- this.debugging =
- {
- "isDebugging" : isDebugging,
- "debuggingMode" : debuggingMode,
- "linefeed" : debuggingMode ? "<br />" : "\n",
- "containerId" : "dubugging-container" + new Date().getTime()
- };
- },
- /* *
- * 传输完毕后自动调用的方法,优先级比用户从run()方法中传入的回调函数高。
- *
- * @public
- */
- onComplete : function ()
- {
- },
- /* *
- * 传输过程中自动调用的方法。
- *
- * @public
- */
- onRunning : function ()
- {
- },
- /* *
- * 调用此方法发送HTTP请求。
- *
- * @public
- * @param {string} url 请求的URL地址
- * @param {mix} params 发送参数
- * @param {Function} callback 回调函数
- * @param {string} ransferMode 请求的方式,有"GET"和"POST"两种
- * @param {string} responseType 响应类型,有"JSON"、"XML"和"TEXT"三种
- * @param {boolean} asyn 是否异步请求的方式
- * @param {boolean} quiet 是否安静模式请求
- */
- run : function (url, params, callback, transferMode, responseType, asyn, quiet)
- {
- /* 处理用户在调用该方法时输入的参数 */
- params = this.parseParams(params);
- transferMode = typeof(transferMode) === "string"
- && transferMode.toUpperCase() === "GET"
- ? "GET"
- : "POST";
- if (transferMode === "GET")
- {
- var d = new Date();
- url += params ? (url.indexOf("?") === - 1 ? "?" : "&") + params : "";
- url = encodeURI(url) + (url.indexOf("?") === - 1 ? "?" : "&") + d.getTime() + d.getMilliseconds();
- params = null;
- }
- responseType = typeof(responseType) === "string" && ((responseType = responseType.toUpperCase()) === "JSON" || responseType === "XML") ? responseType : "TEXT";
- asyn = asyn === false ? false : true;
- /* 处理HTTP请求和响应 */
- var xhr = this.createXMLHttpRequest();
- try
- {
- var self = this;
- if (typeof(self.onRunning) === "function" && !quiet)
- {
- self.onRunning();
- }
- xhr.open(transferMode, url, asyn);
- if (transferMode === "POST")
- {
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- }
- if (asyn)
- {
- xhr.onreadystatechange = function ()
- {
- if (xhr.readyState == 4)
- {
- switch ( xhr.status )
- {
- case 0:
- case 200: // OK!
- /*
- * If the request was to create a new resource
- * (such as post an item to the database)
- * You could instead return a status code of '201 Created'
- */
- if (typeof(self.onComplete) === "function")
- {
- self.onComplete();
- }
- if (typeof(callback) === "function")
- {
- callback.call(self, self.parseResult(responseType, xhr), xhr.responseText);
- }
- break;
- case 304: // Not Modified
- /*
- * This would be used when your Ajax widget is
- * checking for updated content,
- * such as the Twitter interface.
- */
- break;
- case 400: // Bad Request
- /*
- * A bit like a safety net for requests by your JS interface
- * that aren't supported on the server.
- * "Your browser made a request that the server cannot understand"
- */
- alert("XmlHttpRequest status: [400] Bad Request");
- break;
- case 404: // Not Found
- alert("XmlHttpRequest status: [404] \nThe requested URL "+url+" was not found on this server.");
- break;
- case 409: // Conflict
- /*
- * Perhaps your JavaScript request attempted to
- * update a Database record
- * but failed due to a conflict
- * (eg: a field that must be unique)
- */
- break;
- case 503: // Service Unavailable
- /*
- * A resource that this request relies upon
- * is currently unavailable
- * (eg: a file is locked by another process)
- */
- alert("XmlHttpRequest status: [503] Service Unavailable");
- break;
- default:
- alert("XmlHttpRequest status: [" + xhr.status + "] Unknow status.");
- }
- xhr = null;
- }
- }
- if (xhr != null) xhr.send(params);
- }
- else
- {
- if (typeof(self.onRunning) === "function")
- {
- self.onRunning();
- }
- xhr.send(params);
- var result = self.parseResult(responseType, xhr);
- //xhr = null;
- if (typeof(self.onComplete) === "function")
- {
- self.onComplete();
- }
- if (typeof(callback) === "function")
- {
- callback.call(self, result, xhr.responseText);
- }
- return result;
- }
- }
- catch (ex)
- {
- if (typeof(self.onComplete) === "function")
- {
- self.onComplete();
- }
- alert(this.filename + "/run() error:" + ex.description);
- }
- },
- /* *
- * 如果开启了调试模式,该方法会打印出相应的信息。
- *
- * @private
- * @param {string} info 调试信息
- * @param {string} type 信息类型
- */
- displayDebuggingInfo : function (info, type)
- {
- if ( ! this.debugging.debuggingMode)
- {
- alert(info);
- }
- else
- {
- var id = this.debugging.containerId;
- if ( ! document.getElementById(id))
- {
- div = document.createElement("DIV");
- div.id = id;
- div.style.position = "absolute";
- div.style.width = "98%";
- div.style.border = "1px solid #f00";
- div.style.backgroundColor = "#eef";
- var pageYOffset = document.body.scrollTop
- || window.pageYOffset
- || 0;
- div.style.top = document.body.clientHeight * 0.6
- + pageYOffset
- + "px";
- document.body.appendChild(div);
- div.innerHTML = "<div></div>"
- + "<hr style='height:1px;border:1px dashed red;'>"
- + "<div></div>";
- }
- var subDivs = div.getElementsByTagName("DIV");
- if (type === "param")
- {
- subDivs[0].innerHTML = info;
- }
- else
- {
- subDivs[1].innerHTML = info;
- }
- }
- },
- /* *
- * 创建XMLHttpRequest对象的方法。
- *
- * @private
- * @return 返回一个XMLHttpRequest对象
- * @type Object
- */
- createXMLHttpRequest : function ()
- {
- var xhr = null;
- if (window.ActiveXObject)
- {
- var versions = ['Microsoft.XMLHTTP', 'MSXML6.XMLHTTP', 'MSXML5.XMLHTTP', 'MSXML4.XMLHTTP', 'MSXML3.XMLHTTP', 'MSXML2.XMLHTTP', 'MSXML.XMLHTTP'];
- for (var i = 0; i < versions.length; i ++ )
- {
- try
- {
- xhr = new ActiveXObject(versions[i]);
- break;
- }
- catch (ex)
- {
- continue;
- }
- }
- }
- else
- {
- xhr = new XMLHttpRequest();
- }
- return xhr;
- },
- /* *
- * 当传输过程发生错误时将调用此方法。
- *
- * @private
- * @param {Object} xhr XMLHttpRequest对象
- * @param {String} url HTTP请求的地址
- */
- onXMLHttpRequestError : function (xhr, url)
- {
- throw "URL: " + url + "\n"
- + "readyState: " + xhr.readyState + "\n"
- + "state: " + xhr.status + "\n"
- + "headers: " + xhr.getAllResponseHeaders();
- },
- /* *
- * 对将要发送的参数进行格式化。
- *
- * @private
- * @params {mix} params 将要发送的参数
- * @return 返回合法的参数
- * @type string
- */
- parseParams : function (params)
- {
- var legalParams = "";
- params = params ? params : "";
- if (typeof(params) === "string")
- {
- legalParams = params;
- }
- else if (typeof(params) === "object")
- {
- try
- {
- legalParams = "JSON=" + objToJSONString(params);
- }
- catch (ex)
- {
- alert("Can't stringify JSON!");
- return false;
- }
- }
- else
- {
- alert("Invalid parameters!");
- return false;
- }
- if (this.debugging.isDebugging)
- {
- var lf = this.debugging.linefeed,
- info = "[Original Parameters]" + lf + params + lf + lf
- + "[Parsed Parameters]" + lf + legalParams;
- this.displayDebuggingInfo(info, "param");
- }
- return legalParams;
- },
- /* *
- * 对返回的HTTP响应结果进行过滤。
- *
- * @public
- * @params {mix} result HTTP响应结果
- * @return 返回过滤后的结果
- * @type string
- */
- preFilter : function (result)
- {
- return result.replace(/\xEF\xBB\xBF/g, "");
- },
- /* *
- * 对返回的结果进行格式化。
- *
- * @private
- * @return 返回特定格式的数据结果
- * @type mix
- */
- parseResult : function (responseType, xhr)
- {
- var result = null;
- switch (responseType)
- {
- case "JSON" :
- result = this.preFilter(xhr.responseText);
- try
- {
- result = parseObjectToJSON(result);
- }
- catch (ex)
- {
- throw this.filename + "/parseResult() error: can't parse to JSON.\n\n" + xhr.responseText;
- }
- break;
- case "XML" :
- result = xhr.responseXML;
- break;
- case "TEXT" :
- result = this.preFilter(xhr.responseText);
- break;
- default :
- throw this.filename + "/parseResult() error: unknown response type:" + responseType;
- }
- if (this.debugging.isDebugging)
- {
- var lf = this.debugging.linefeed,
- info = "[Response Result of " + responseType + " Format]" + lf
- + result;
- if (responseType === "JSON")
- {
- info = "[Response Result of TEXT Format]" + lf
- + xhr.responseText + lf + lf
- + info;
- }
- this.displayDebuggingInfo(info, "result");
- }
- return result;
- }
- };
- /* 定义两个别名 */
- var Ajax = Transport;
- Ajax.call = Transport.run;
- /*
- json.js
- 2007-03-06
- Public Domain
- This file adds these methods to JavaScript:
- array.toJSONString()
- boolean.toJSONString()
- date.toJSONString()
- number.toJSONString()
- object.toJSONString()
- string.toJSONString()
- These methods produce a JSON text from a JavaScript value.
- It must not contain any cyclical references. Illegal values
- will be excluded.
- The default conversion for dates is to an ISO string. You can
- add a toJSONString method to any date object to get a different
- representation.
- string.parseJSON(filter)
- This method parses a JSON text to produce an object or
- array. It can throw a SyntaxError exception.
- The optional filter parameter is a function which can filter and
- transform the results. It receives each of the keys and values, and
- its return value is used instead of the original value. If it
- returns what it received, then structure is not modified. If it
- returns undefined then the member is deleted.
- Example:
- // Parse the text. If a key contains the string 'date' then
- // convert the value to a date.
- myData = text.parseJSON(function (key, value) {
- return key.indexOf('date') >= 0 ? new Date(value) : value;
- });
- It is expected that these methods will formally become part of the
- JavaScript Programming Language in the Fourth Edition of the
- ECMAScript standard in 2008.
- */
- // Augment the basic prototypes if they have not already been augmented.
- /*if ( ! Object.prototype.toJSONString) {
- Array.prototype.toJSONString = function () {
- var a = ['['], // The array holding the text fragments.
- b, // A boolean indicating that a comma is required.
- i, // Loop counter.
- l = this.length,
- v; // The value to be stringified.
- function p(s) {
- // p accumulates text fragments in an array. It inserts a comma before all
- // except the first fragment.
- if (b) {
- a.push(',');
- }
- a.push(s);
- b = true;
- }
- // For each value in this array...
- for (i = 0; i < l; i ++) {
- v = this[i];
- switch (typeof v) {
- // Values without a JSON representation are ignored.
- case 'undefined':
- case 'function':
- case 'unknown':
- break;
- // Serialize a JavaScript object value. Ignore objects thats lack the
- // toJSONString method. Due to a specification error in ECMAScript,
- // typeof null is 'object', so watch out for that case.
- case 'object':
- if (v) {
- if (typeof v.toJSONString === 'function') {
- p(v.toJSONString());
- }
- } else {
- p("null");
- }
- break;
- // Otherwise, serialize the value.
- default:
- p(v.toJSONString());
- }
- }
- // Join all of the fragments together and return.
- a.push(']');
- return a.join('');
- };
- Boolean.prototype.toJSONString = function () {
- return String(this);
- };
- Date.prototype.toJSONString = function () {
- // Ultimately, this method will be equivalent to the date.toISOString method.
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
- return '"' + this.getFullYear() + '-' +
- f(this.getMonth() + 1) + '-' +
- f(this.getDate()) + 'T' +
- f(this.getHours()) + ':' +
- f(this.getMinutes()) + ':' +
- f(this.getSeconds()) + '"';
- };
- Number.prototype.toJSONString = function () {
- // JSON numbers must be finite. Encode non-finite numbers as null.
- return isFinite(this) ? String(this) : "null";
- };
- Object.prototype.toJSONString = function () {
- var a = ['{'], // The array holding the text fragments.
- b, // A boolean indicating that a comma is required.
- k, // The current key.
- v; // The current value.
- function p(s) {
- // p accumulates text fragment pairs in an array. It inserts a comma before all
- // except the first fragment pair.
- if (b) {
- a.push(',');
- }
- a.push(k.toJSONString(), ':', s);
- b = true;
- }
- // Iterate through all of the keys in the object, ignoring the proto chain.
- for (k in this) {
- if (this.hasOwnProperty(k)) {
- v = this[k];
- switch (typeof v) {
- // Values without a JSON representation are ignored.
- case 'undefined':
- case 'function':
- case 'unknown':
- break;
- // Serialize a JavaScript object value. Ignore objects that lack the
- // toJSONString method. Due to a specification error in ECMAScript,
- // typeof null is 'object', so watch out for that case.
- case 'object':
- if (this !== window)
- {
- if (v) {
- if (typeof v.toJSONString === 'function') {
- p(v.toJSONString());
- }
- } else {
- p("null");
- }
- }
- break;
- default:
- p(v.toJSONString());
- }
- }
- }
- // Join all of the fragments together and return.
- a.push('}');
- return a.join('');
- };
- (function (s) {
- // Augment String.prototype. We do this in an immediate anonymous function to
- // avoid defining global variables.
- // m is a table of character substitutions.
- var m = {
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- };
- s.parseJSON = function (filter) {
- // Parsing happens in three stages. In the first stage, we run the text against
- // a regular expression which looks for non-JSON characters. We are especially
- // concerned with '()' and 'new' because they can cause invocation, and '='
- // because it can cause mutation. But just to be safe, we will reject all
- // unexpected characters.
- try {
- if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
- test(this)) {
- // In the second stage we use the eval function to compile the text into a
- // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
- // in JavaScript: it can begin a block or an object literal. We wrap the text
- // in parens to eliminate the ambiguity.
- var j = eval('(' + this + ')');
- // In the optional third stage, we recursively walk the new structure, passing
- // each name/value pair to a filter function for possible transformation.
- if (typeof filter === 'function') {
- function walk(k, v) {
- if (v && typeof v === 'object') {
- for (var i in v) {
- if (v.hasOwnProperty(i)) {
- v[i] = walk(i, v[i]);
- }
- }
- }
- return filter(k, v);
- }
- j = walk('', j);
- }
- return j;
- }
- } catch (e) {
- // Fall through if the regexp test fails.
- }
- throw new SyntaxError("parseJSON");
- };
- s.toJSONString = function () {
- // If the string contains no control characters, no quote characters, and no
- // backslash characters, then we can simply slap some quotes around it.
- // Otherwise we must also replace the offending characters with safe
- // sequences.
- // add by weberliu @ 2007-4-2
- var _self = this.replace("&", "%26");
- if (/["\\\x00-\x1f]/.test(this)) {
- return '"' + _self.replace(/([\x00-\x1f\\"])/g, function(a, b) {
- var c = m[b];
- if (c) {
- return c;
- }
- c = b.charCodeAt();
- return '\\u00' +
- Math.floor(c / 16).toString(16) +
- (c % 16).toString(16);
- }) + '"';
- }
- return '"' + _self + '"';
- };
- })(String.prototype);
- }*/
- function objToJSONString(obj, filter){
- return JSON.stringify(obj, filter);
- }
- function parseObjectToJSON(object, filter){
- return JSON.parse(object, filter);
- }
- Ajax.onRunning = showLoader;
- Ajax.onComplete = hideLoader;
- /* *
- * 显示载入信息
- */
- function showLoader()
- {
- document.getElementsByTagName('body').item(0).style.cursor = "wait";
- if (top.frames['header-frame'] && top.frames['header-frame'].document.getElementById("load-div"))
- {
- top.frames['header-frame'].document.getElementById("load-div").style.display = "block";
- }
- else
- {
- var obj = document.getElementById('loader');
- if ( ! obj && typeof(process_request) != 'undefined')
- {
- obj = document.createElement("DIV");
- obj.id = "loader";
- obj.innerHTML = process_request;
- document.body.appendChild(obj);
- }
- }
- }
- /* *
- * 隐藏载入信息
- */
- function hideLoader()
- {
- document.getElementsByTagName('body').item(0).style.cursor = "auto";
- if (top.frames['header-frame'] && top.frames['header-frame'].document.getElementById("load-div"))
- {
- setTimeout(function(){top.frames['header-frame'].document.getElementById("load-div").style.display = "none"}, 10);
- }
- else
- {
- try
- {
- var obj = document.getElementById("loader");
- obj.style.display = 'none';
- document.body.removeChild(obj);
- }
- catch (ex)
- {}
- }
- }