NS库

NS库,强大的扩展

이 스크립트는 직접 설치해서 쓰는 게 아닙니다. 다른 스크립트가 메타 명령 // @require https://update.greasyfork.org/scripts/376730/662553/NS%E5%BA%93.js(으)로 포함하여 쓰는 라이브러리입니다.

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name         NS库
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  NS库,强大的扩展
// @author       lnwazg
// @grant        none
// ==/UserScript==
(function (window) {
	"use strict";

	var NS = {
		//属性表
		//浏览器判断
		browser: {
			//判断浏览器类型
			versions: (function () {
				var u = navigator.userAgent,
					app = navigator.appVersion;
				return {
					//是否是以下类型的浏览器
					iOS: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
					android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1,
					iPhone: u.indexOf('iPhone') > -1,
					iPad: u.indexOf('iPad') > -1,
					IE: (!!window.ActiveXObject || "ActiveXObject" in window || u.indexOf("MSIE") >= 1),
					Firefox: (u.indexOf("Firefox") != -1),
					Chrome: (u.indexOf("Chrome") != -1),
					Safari: (u.indexOf("Safari") != -1)
				};
			})()
		},
		//localStorage方便的操作类
		LS: {
			set: function (key, value) {
				//在iPhone/iPad上有时设置setItem()时会出现诡异的QUOTA_EXCEEDED_ERR错误
				//这时一般在setItem之前,先removeItem()就ok了
				if (this.get(key) !== null)
					this.remove(key);
				localStorage.setItem(key, value);
			},
			//查询不存在的key时,有的浏览器返回undefined,这里统一返回null
			get: function (key) {
				var v = localStorage.getItem(key);
				return v === undefined ? null : v;
			},
			remove: function (key) {
				localStorage.removeItem(key);
			},
			clear: function () {
				localStorage.clear();
			},
			each: function (fn) {
				var n = localStorage.length,
					i = 0,
					fn = fn || function () {},
					key;
				for (; i < n; i++) {
					key = localStorage.key(i);
					if (fn.call(this, key, this.get(key)) === false)
						break;
					//如果内容被删除,则总长度和索引都同步减少
					if (localStorage.length < n) {
						n--;
						i--;
					}
				}
			}
		},
		//TODO: 其它新增属性可添加于此

		//函数表
		//localStorage操作的快捷方式
		lset: function () {
			this.LS.set.apply(this.LS, arguments);
		},
		lget: function () {
			this.LS.get.apply(this.LS, arguments);
		},
		lremove: function () {
			this.LS.remove.apply(this.LS, arguments);
		},
		lclear: function () {
			this.LS.clear.apply(this.LS, arguments);
		},
		leach: function () {
			this.LS.each.apply(this.LS, arguments);
		},
		//读取cookies
		getCookie: function (name) {
			var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
			if (arr = document.cookie.match(reg)) {
				return unescape(arr[2]);
			} else {
				return null;
			}
		},
		/**
		 * 动态获取cookie的域的方法<br>
		 * 该方法可作为适配器方法
		 */
		getCookieDomain: function () {
			//首先你应该预置一个NS.cookieDomainFn的函数,否则将使用空默认值
			//		return (NS.cookieDomain? (";domain="+ NS.cookieDomain):"");
			return (NS.cookieDomainFn ? (";domain=" + NS.cookieDomainFn()) : "");
		},
		/**
		 * 写cookies
		 * name键
		 * value值
		 */
		setCookie: function (name, value, expireDays) {
			//(默认为永远不过期)写入之后有效期设置为100年,也就是相当于永不过期
			var Days = 36000;
			if (expireDays) {
				//假如传了过期时间这个参数
				Days = expireDays;
			}
			var exp = new Date();
			exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
			//设置cookie的键值对信息以及过期信息 (指定path真的很重要!) 
			document.cookie = (name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/" + this.getCookieDomain());
		},
		//写cookie。该cookie的生命周期为:session
		setCookieInSession: function (name, value) {
			var isIE = !-[1, ]; //判断是否是ie核心浏览器  
			if (isIE) {
				//设置cookie的键值对信息以及过期信息 (指定path真的很重要!) 
				document.cookie = (name + "=" + escape(value) + ";expires=At the end of the Session;path=/" + this.getCookieDomain());
			} else {
				//设置cookie的键值对信息以及过期信息 (指定path真的很重要!) 
				document.cookie = (name + "=" + escape(value) + ";expires=Session;path=/" + this.getCookieDomain());
			}
		},
		//删除cookies   
		delCookie: function (name) {
			var exp = new Date();
			exp.setTime(exp.getTime() - 1); //将过期时间设置为上一毫秒   
			var cval = this.getCookie(name);
			if (cval != null) {
				//若能取到这个cookie的键值对,那么就将其设置为过期    (指定path真的很重要!) 
				document.cookie = (name + "=" + cval + ";expires=" + exp.toGMTString() + ";path=/" + this.getCookieDomain());
			}
		},
		//删除cookies,未指定域名的
		delCookieWithoutDomain: function (name) {
			var exp = new Date();
			exp.setTime(exp.getTime() - 1); //将过期时间设置为上一毫秒   
			var cval = this.getCookie(name);
			if (cval != null) {
				//若能取到这个cookie的键值对,那么就将其设置为过期    (指定path真的很重要!) 
				document.cookie = (name + "=" + cval + ";expires=" + exp.toGMTString() + ";path=/");
			}
		},
		//判断某个字符串是否为空
		isNull: function (obj) {
			if (!obj || obj.length == 0) {
				return true;
			}
			return false;
		},
		//判断是否为空
		isEmpty: function (obj) {
			return this.isNull(obj);
		},
		/**
		 * 是否不为空
		 */
		isNotEmpty: function (obj) {
			return !this.isNull(obj);
		},
		//某个数组中是否包含某个元素
		contains: function (element, array) {
			if (this.indexOf(element, array) != -1) {
				return true;
			}
			return false;
		},
		/**
		 * 更适用于对象比较的版本
		 * @param element
		 * @param array
		 */
		objContains: function (element, array) {
			if (this.objIndexOf(element, array) != -1) {
				return true;
			}
			return false;
		},
		//返回某个元素在数组中的位置
		indexOf: $.inArray,
		/**
		 * 更适用于对象比较的版本<br>
		 * 将每个元素分别转换成string之后再进行查找。(此种查找方法适用于复杂对象的查找,并且那种情况下indexOf()方法将会失效)
		 */
		objIndexOf: function (element, array) {
			var eleNew = JSON.stringify(element);
			var arrayNew = [];
			for (var i = 0; i < array.length; i++) {
				arrayNew[i] = JSON.stringify(array[i]);
			}
			return this.indexOf(eleNew, arrayNew);
		},
		/**
		 * 删除数组中某个索引的数据
		 * @param idx
		 * @param array
		 * @returns
		 */
		removeIdx: function (idx, array) {
			if (isNaN(idx) || idx > array.length) {
				return false;
			}
			for (var i = 0, n = 0; i < array.length; i++) {
				if (array[i] != array[idx]) {
					array[n++] = array[i];
				}
			}
			array.length -= 1; //如果数组已经没元素了,再删除就会报错!
			return array;
		},
		/**
		 * 更适用于对象比较的版本
		 * @param idx
		 * @param array
		 * @returns
		 */
		objRemoveIdx: function (idx, array) {
			return this.removeIdx(idx, array);
		},
		/**
		 * 删除数组中的某个元素
		 * @param element
		 * @param array
		 * @returns
		 */
		removeElement: function (element, array) {
			return this.removeIdx(this.indexOf(element, array), array);
		},
		/**
		 * 更适用于对象比较的版本<br>
		 * 删除数组中的某个元素
		 * @param element
		 * @param array
		 * @returns
		 */
		objRemoveElement: function (element, array) {
			return this.objRemoveIdx(this.objIndexOf(element, array), array);
		},
		/**
		 * 判断某个字符串是不是以某个字符串开头
		 * 为何要写这个方法?因为令人无语的微信浏览器竟然不支持该方法啊!!!
		 * @param original  原有的字符串
		 * @param str    待搜索的字符串
		 * @returns {Boolean}
		 */
		startsWith: function (original, str) {
			if (str == null || str == "" || original.length == 0 || str.length > original.length) {
				return false;
			}
			if (original.substr(0, str.length) == str) {
				return true;
			} else {
				return false;
			}
			return true;
		},
		/**
		 * 判断某个字符串是不是以某个字符串结尾
		 * @param original
		 * @param str
		 * @returns {Boolean}
		 */
		endWith: function (original, str) {
			if (str == null || str == "" || original.length == 0 || str.length > original.length) {
				return false;
			}
			if (original.substring(original.length - str.length) == str) {
				return true;
			} else {
				return false;
			}
			return true;
		},
		//返回短名称
		shortName: function (str, maxLength) {
			if (str && str.length > 0) {
				var len = str.length;
				if (len <= maxLength) {
					return str;
				} else {
					return (str.substr(0, maxLength) + "...");
				}
			} else {
				return "";
			}
		},
		//检查是否是数字
		checkNumber: function (value) {
			var reg = /^[1-9]\d*$/;
			if ("0" == value || reg.test(value)) {
				return true;
			} else {
				return false;
			}
		},
		//将指定map的指定的key初始化成空array
		initMapArray: function () {
			var len = arguments.length; //可变参数的方法
			if (len == 0 || len == 1) {
				return;
			}
			var map = arguments[0];
			for (var i = 1; i < arguments.length; i++) {
				var paramName = arguments[i];
				map[paramName] = [];
			}
		},
		//匿名
		anoym: function (name) {
			if (!name || name.length == 0) {
				return "***";
			}
			var length = name.length;
			if (length == 1) {
				return (name + "***");
			} else {
				return (name[0] + "***" + name[name.length - 1]);
			}
		},
		/**
		 * 动态加载脚本
		 * 不推荐使用,因为这样加载的脚本会到最后才执行。如果该脚本被其他脚本依赖,则不推荐这样的方式
		 * @param script
		 */
		loadScript: function (url) {
			var content = '<script type="text/javascript" src="' + url + '"></script>';
			document.writeln(content);
		},
		/**
		 * 动态加载样式
		 * @param url
		 */
		loadCss: function (url) {
			var content = '<link rel="stylesheet" type="text/css" href="' + url + '"/>';
			document.writeln(content);
		},
		//转换为用于显示用的字符串(去除undefined等等不合理的显示)
		toStr: function (obj) {
			if (this.isNull(obj)) {
				return "";
			}
			return obj;
		},
		//兼容方式获取滚动条距离顶部的距离
		getScrollTop: function () {
			var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
			return scrollTop;
		},
		//兼容方式设置滚动条距离顶部的距离
		setScrollTop: function (scroll_top) {
			document.documentElement.scrollTop = scroll_top;
			window.pageYOffset = scroll_top;
			document.body.scrollTop = scroll_top;
		},
		/**
		 * 获取两个日期的天数差(起始时间-结束时间) 
		 */
		getDateDiffDays: function (startDate, endDate) {
			var startTime = new Date(Date.parse(startDate.replace(/-/g, "/"))).getTime(); //得到毫秒数     
			var endTime = new Date(Date.parse(endDate.replace(/-/g, "/"))).getTime();
			var dates = Math.abs((startTime - endTime)) / (1000 * 60 * 60 * 24); //取绝对值     
			return dates;
		},
		//防重复提交的事件注册表
		//用于检测某个事件是否已经在队列里先行一步了
		_timerReg: {},
		/**
		 * 防止快速点击所导致的重复提交
		 * @param id 标记,用于区分是否是同一种业务场景
		 * @param fn 回调函数
		 * @param wait 等待的毫秒数
		 * @returns
		 */
		preventDuplicateSubmission: function (id, fn, wait) {
			if (!wait) {
				wait = 300; //默认延迟时间是300毫秒
			}
			if (NS._timerReg[id]) {
				console.log("检测到重复点击...");
				window.clearTimeout(NS._timerReg[id]);
				delete NS._timerReg[id];
			}
			return NS._timerReg[id] = window.setTimeout(function () {
				fn();
				delete NS._timerReg[id];
			}, wait);
		},
		/**
		 * 将对象保存到localStorage
		 * @param key
		 * @param obj
		 */
		saveLocalStorageObj: function (key, obj) {
			if (localStorage) {
				localStorage[key] = JSON.stringify(obj);
			}
		},
		/**
		 * 从localStorage读取出对象
		 * @param key
		 */
		readLocalStorageObj: function (key) {
			if (localStorage && localStorage[key]) {
				if (NS.isNotEmpty(localStorage[key])) {
					return JSON.parse(localStorage[key]);
				}
			}
			return null;
		},
		/**
		 * 指定的Url中,如果不存在某个参数,则添加该参数
		 */
		appendUrlParamIfNotExist: function (url, name, value) {
			if (!this.urlHasParam(url, name)) {
				//没有参数,则加上参数。有参数,则什么都不做
				url = this.urlAppendParam(url, name, value);
			}
			return url;
		},
		/**
		 * 给指定的url增加参数
		 * @param url
		 * @param name
		 * @param value
		 */
		urlAppendParam: function (url, name, value) {
			url += ((url.indexOf("?") == -1 ? "?" : "&") + name + "=" + value);
			return url;
		},
		//某个url中,是否含有某个参数
		urlHasParam: function (url, name) {
			var paramArray = this.getUrlParamArray(url);
			if (this.contains(name, paramArray)) {
				return true;
			} else {
				return false;
			}
		},
		/**
		 * 获取指定URL的参数的数组
		 * 只要是在这个URL中出现过的参数,都可以算在里面
		 * @param url
		 * @returns {Object}
		 */
		getUrlParamArray: function (url) {
			var retArray = [];
			if (url.indexOf("?") != -1) {
				var str = url.substr(url.indexOf("?") + 1);
				strs = str.split("&");
				for (var i = 0; i < strs.length; i++) {
					//	         theRequest[strs[i].split("=")[0]]=(strs[i].split("=")[1]);
					retArray.push(strs[i].split("=")[0]);
				}
			}
			return retArray;
		},
		/**
		 * 替换所有字符
		 * @param str 
		 * @param reallyDo 想要替换的
		 * @param replaceWith 替换成的
		 * @param ignoreCase 是否忽略大小写
		 * @returns
		 */
		replaceAll: function (str, reallyDo, replaceWith, ignoreCase) {
			if (!RegExp.prototype.isPrototypeOf(reallyDo)) {
				return str.replace(new RegExp(reallyDo, (ignoreCase ? "gi" : "g")), replaceWith);
			} else {
				return str.replace(reallyDo, replaceWith);
			}
		},
		/**
		 * 为文字包裹上一层颜色
		 */
		wrapperColor: function (original, color) {
			return "<font color='" + color + "'>" + original + "</font>";
		},
		/**
		 * 将毫秒数格式化成正常显示的格式
		 */
		formatDateMillsToShow: function (millseconds) {
			if (this.isEmpty(millseconds)) {
				return "";
			}
			return (new Date(millseconds)).format("yyyy-MM-dd hh:mm:ss");
		},
		formatDateMillsToShowDate: function (millseconds) {
			if (this.isEmpty(millseconds)) {
				return "";
			}
			return (new Date(millseconds)).format("yyyy-MM-dd");
		},
		/**
		 * 一个闭包计数器工厂<br>
		 * 默认从1开始计数<br>
		 * 可以自己指定开始的计数值
		 * @param startNum
		 * @returns {Function}
		 */
		counterFnFactory: function (startNum) {
			var num = 1;
			if (typeof startNum !== 'undefined') {
				num = Number(startNum);
			}
			//获取下一个数(默认返回的方法)
			var getNextNumFn = function () {
				return num++;
			}
			return getNextNumFn;
		},
		/**
		 * dom中是否存在某个selector<br>
		 * 可以存在一个或者多个
		 * @param selector
		 * @returns {Boolean}
		 */
		existsDomNode: function (selector) {
			return $(selector).get().length > 0;
		},
		/**
		 * dom中是否有且只存在一个某个selector
		 * @param selector
		 * @returns {Boolean}
		 */
		existsOnlyOneDomNode: function (selector) {
			return $(selector).get().length == 1;
		},
		/**
		 * 将数字格式转换成千分位
		 *@param{Object}num
		 */
		commafy: function (num) {
			if ((num + "").trim() == "") {
				return "";
			}
			if (isNaN(num)) {
				return "";
			}
			num = num + "";
			if (/^.*\..*$/.test(num)) {
				var pointIndex = num.lastIndexOf(".");
				var intPart = num.substring(0, pointIndex);
				var pointPart = num.substring(pointIndex + 1, num.length);
				intPart = intPart + "";
				var re = /(-?\d+)(\d{3})/
				while (re.test(intPart)) {
					intPart = intPart.replace(re, "$1,$2")
				}
				num = intPart + "." + pointPart;
			} else {
				num = num + "";
				var re = /(-?\d+)(\d{3})/
				while (re.test(num)) {
					num = num.replace(re, "$1,$2")
				}
			}
			return num;
		},
		/**
		 * 对千分位货币值去除千分位
		 *@param{Object}num
		 */
		delcommafy: function (num) {
			if ((num + "").trim() == "") {
				return "";
			}
			num = num.replace(/,/gi, '');
			return num;
		},
		/**
		 * 获取用户识别码<br>
		 * 该识别码每次调用的返回值均不相同<br>
		 * 该功能主要服务于统一登录系统,用于获取验证码的凭证
		 * @returns
		 */
		getIdentity: function () {
			var s = [];
			var hexDigits = "0123456789abcdef";
			for (var i = 0; i < 36; i++) {
				s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
			}
			s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
			s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the
			// clock_seq_hi_and_reserved
			// to 01
			s[8] = s[13] = s[18] = s[23] = "-";
			var uid = s.join("");
			var identity = window.Base64.encode(uid);
			NS.currentIdentity = identity; //设置系统当前的识别码
			return identity;
		},
		getCurrentIdentity: function () {
			return NS.currentIdentity;
		},
		/**
		 * 绑定系统的回车事件
		 * @param fn
		 */
		bindGlobalEnterEvent: function (fn) {
			$(document).keypress(function (e) {
				if (e.which == 13) {
					fn();
				}
			});
		},
		//从页面上提取数据到对象中
		getPageValueToObj: function (obj, toHandleArray) {
			//		var toHandleArray = ['insure_sex_limit','insure_min_age','insure_min_age_unit','insure_max_age','insure_max_age_unit'];
			//		//获取页面中的字段信息
			////		obj.insure_sex_limit = $.trim($("#insure_sex_limit").val());
			////		obj.insure_min_age= $.trim($("#insure_min_age").val());
			////		obj.insure_min_age_unit= $.trim($("#insure_min_age_unit").val());
			////		obj.insure_max_age= $.trim($("#insure_max_age").val());
			////		obj.insure_max_age_unit= $.trim($("#insure_max_age_unit").val());
			if (obj && toHandleArray) {
				toHandleArray.forEach(function (e) {
					obj[e] = $.trim($("#" + e).val());
				});
				return obj;
			} else {
				return null;
			}
		},
		//将对象的属性值设置到页面中
		setObjPropToPageValue: function (extraData, toHandleArray) {
			if (extraData && toHandleArray) {
				toHandleArray.forEach(function (e) {
					$("#" + e).val(extraData[e]);
				});
			}
		},
		//获取某个字符串的hashCode
		getHashCode: function (str, caseSensitive) {
			if (!caseSensitive) {
				str = str.toLowerCase();
			}
			// 1315423911=b'1001110011001111100011010100111'
			var hash = 1315423911,
				i, ch;
			for (i = str.length - 1; i >= 0; i--) {
				ch = str.charCodeAt(i);
				hash ^= ((hash << 5) + ch + (hash >> 2));
			}
			return (hash & 0x7FFFFFFF);
		},
		getCurrentDate: function () {
			return (new Date()).format("yyyy-MM-dd");
		},
		getCurrentDateTime: function () {
			return (new Date()).format("yyyy-MM-dd hh:mm:ss");
		},
		//日志模块
		//日志开关
		__isLogDebug__: true,
		//关闭日志开关
		disableLog: function () {
			NS.__isLogDebug__ = false;
		},
		enableLog: function () {
			NS.__isLogDebug__ = true;
		},
		//简易日志模块
		log: function () {
			if (NS.__isLogDebug__ && arguments.length > 0) {
				console.log.apply(console, arguments);
			}
		},
		//base64加解密
		base64encode: function (input) {
			var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
			input = escape(input);
			var output = "";
			var chr1, chr2, chr3 = "";
			var enc1, enc2, enc3, enc4 = "";
			var i = 0;
			do {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);
				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;
				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}
				output = output +
					keyStr.charAt(enc1) +
					keyStr.charAt(enc2) +
					keyStr.charAt(enc3) +
					keyStr.charAt(enc4);
				chr1 = chr2 = chr3 = "";
				enc1 = enc2 = enc3 = enc4 = "";
			} while (i < input.length);
			return output;
		},
		base64decode: function (input) {
			var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
			var output = "";
			var chr1, chr2, chr3 = "";
			var enc1, enc2, enc3, enc4 = "";
			var i = 0;
			// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
			var base64test = /[^A-Za-z0-9\+\/\=]/g;
			if (base64test.exec(input)) {
				alert("There were invalid base64 characters in the input text.\n" +
					"Valid base64 characters are A-Z, a-z, 0-9, '+', '/', and '='\n" +
					"Expect errors in decoding.");
			}
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
			do {
				enc1 = keyStr.indexOf(input.charAt(i++));
				enc2 = keyStr.indexOf(input.charAt(i++));
				enc3 = keyStr.indexOf(input.charAt(i++));
				enc4 = keyStr.indexOf(input.charAt(i++));
				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;
				output = output + String.fromCharCode(chr1);
				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}
				chr1 = chr2 = chr3 = "";
				enc1 = enc2 = enc3 = enc4 = "";
			} while (i < input.length);
			return unescape(output);
		},
		//localStorage数据导入导出
		//TODO json支持、垫片技术
		exportLocalStorageData: function () {
			console.log("开始导出localStorage配置信息...")
			var obj = {};
			NS.LS.each(function (k, v) {
				obj[k] = v;
			});
			return NS.base64encode(JSON.stringify(obj));
		},
		importLocalStorageData: function (input) {
			console.log("开始导入localStorage配置信息...")
			var jsonStr = NS.base64decode(input);
			var obj = JSON.parse(jsonStr);
			// NS.LS.clear();
			Object.keys(obj).forEach(function (k) {
				NS.LS.set(k, obj[k]);
			});
		},
		//TODO: 其它新增函数可添加于此

		//语言扩展表
		_ext: {
			/**
			 * js语言级别的扩展
			 */
			jsLangExt: function () {
				//为IE浏览器补丁begin===============================
				//兼容IE8、IE9等低版本浏览器找不到console对象的写法
				window.console = window.console || (function () {
					var c = {};
					c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile = c.clear = c.exception = c.trace = c.assert = function () {};
					return c;
				})();

				if (NS.browser.versions.IE) {
					console.log("检测到IE浏览器...");
				}

				//console.log("加载扩展[jsLangExt]");

				if (!window.getComputedStyle) {
					window.getComputedStyle = function (el, pseudo) {
						this.el = el;
						this.getPropertyValue = function (prop) {
							var re = /(\-([a-z]){1})/g;
							if (prop == 'float') prop = 'styleFloat';
							if (re.test(prop)) {
								prop = prop.replace(re, function () {
									return arguments[2].toUpperCase();
								});
							}
							return el.currentStyle[prop] ? el.currentStyle[prop] : null;
						}
						return this;
					}
				}

				if (!Array.prototype.forEach) {
					Array.prototype.forEach = function (fun /*, thisp*/ ) {
						var len = this.length;
						if (typeof fun != "function")
							throw new TypeError();
						var thisp = arguments[1];
						for (var i = 0; i < len; i++) {
							if (i in this)
								fun.call(thisp, this[i], i, this);
						}
					};
				}

				if (!Array.prototype.some) {
					Array.prototype.some = function (fun /*, thisArg */ ) {
						'use strict';
						if (this === void 0 || this === null)
							throw new TypeError();
						var t = Object(this);
						var len = t.length >>> 0;
						if (typeof fun !== 'function')
							throw new TypeError();

						var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
						for (var i = 0; i < len; i++) {
							if (i in t && fun.call(thisArg, t[i], i, t))
								return true;
						}
						return false;
					};
				}

				if (!Array.prototype.every) {
					Array.prototype.every = function (fun /*, thisArg */ ) {
						'use strict';
						if (this === void 0 || this === null)
							throw new TypeError();
						var t = Object(this);
						var len = t.length >>> 0;
						if (typeof fun !== 'function')
							throw new TypeError();
						var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
						for (var i = 0; i < len; i++) {
							if (i in t && !fun.call(thisArg, t[i], i, t))
								return false;
						}
						return true;
					};
				}

				// Production steps of ECMA-262, Edition 5, 15.4.4.19
				// Reference: http://es5.github.io/#x15.4.4.19
				if (!Array.prototype.map) {
					Array.prototype.map = function (callback, thisArg) {
						var T, A, k;
						if (this == null) {
							throw new TypeError(' this is null or not defined');
						}
						// 1. Let O be the result of calling ToObject passing the |this| 
						//    value as the argument.
						var O = Object(this);

						// 2. Let lenValue be the result of calling the Get internal 
						//    method of O with the argument "length".
						// 3. Let len be ToUint32(lenValue).
						var len = O.length >>> 0;

						// 4. If IsCallable(callback) is false, throw a TypeError exception.
						// See: http://es5.github.com/#x9.11
						if (typeof callback !== 'function') {
							throw new TypeError(callback + ' is not a function');
						}

						// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
						if (arguments.length > 1) {
							T = thisArg;
						}

						// 6. Let A be a new array created as if by the expression new Array(len) 
						//    where Array is the standard built-in constructor with that name and 
						//    len is the value of len.
						A = new Array(len);

						// 7. Let k be 0
						k = 0;

						// 8. Repeat, while k < len
						while (k < len) {

							var kValue, mappedValue;

							// a. Let Pk be ToString(k).
							//   This is implicit for LHS operands of the in operator
							// b. Let kPresent be the result of calling the HasProperty internal 
							//    method of O with argument Pk.
							//   This step can be combined with c
							// c. If kPresent is true, then
							if (k in O) {

								// i. Let kValue be the result of calling the Get internal 
								//    method of O with argument Pk.
								kValue = O[k];

								// ii. Let mappedValue be the result of calling the Call internal 
								//     method of callback with T as the this value and argument 
								//     list containing kValue, k, and O.
								mappedValue = callback.call(T, kValue, k, O);

								// iii. Call the DefineOwnProperty internal method of A with arguments
								// Pk, Property Descriptor
								// { Value: mappedValue,
								//   Writable: true,
								//   Enumerable: true,
								//   Configurable: true },
								// and false.

								// In browsers that support Object.defineProperty, use the following:
								// Object.defineProperty(A, k, {
								//   value: mappedValue,
								//   writable: true,
								//   enumerable: true,
								//   configurable: true
								// });

								// For best browser support, use the following:
								A[k] = mappedValue;
							}
							// d. Increase k by 1.
							k++;
						}

						// 9. return A
						return A;
					};
				}
				if (!Array.prototype.filter) {
					Array.prototype.filter = function (fun /*, thisArg */ ) {
						"use strict";
						if (this === void 0 || this === null)
							throw new TypeError();
						var t = Object(this);
						var len = t.length >>> 0;
						if (typeof fun !== "function")
							throw new TypeError();
						var res = [];
						var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
						for (var i = 0; i < len; i++) {
							if (i in t) {
								var val = t[i];
								if (fun.call(thisArg, val, i, t))
									res.push(val);
							}
						}
						return res;
					};
				}
				if (!Function.prototype.bind) {
					Function.prototype.bind = function (oThis) {
						if (typeof this !== "function") {
							// closest thing possible to the ECMAScript 5 internal IsCallable function
							throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
						}

						var aArgs = Array.prototype.slice.call(arguments, 1),
							fToBind = this,
							fNOP = function () {},
							fBound = function () {
								return fToBind.apply(this instanceof fNOP && oThis ?
									this :
									oThis || window,
									aArgs.concat(Array.prototype.slice.call(arguments)));
							};

						fNOP.prototype = this.prototype;
						fBound.prototype = new fNOP();

						return fBound;
					};
				}
				if (!Array.indexOf) {
					Array.prototype.indexOf = function (obj) {
						for (var i = 0; i < this.length; i++) {
							if (this[i] == obj) {
								return i;
							}
						}
						return -1;
					}
				}

				// Object.keys
				if (!Object.keys) {
					Object.keys = (function () {
						'use strict';

						// modified from https://github.com/es-shims/object-keys

						var has = Object.prototype.hasOwnProperty;
						var toStr = Object.prototype.toString;
						var isEnumerable = Object.prototype.propertyIsEnumerable;
						var hasDontEnumBug = !isEnumerable.call({
							toString: null
						}, 'toString');
						var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
						var dontEnums = [
							'toString',
							'toLocaleString',
							'valueOf',
							'hasOwnProperty',
							'isPrototypeOf',
							'propertyIsEnumerable',
							'constructor'
						];
						var equalsConstructorPrototype = function (o) {
							var ctor = o.constructor;
							return ctor && ctor.prototype === o;
						};
						var excludedKeys = {
							$console: true,
							$external: true,
							$frame: true,
							$frameElement: true,
							$frames: true,
							$innerHeight: true,
							$innerWidth: true,
							$outerHeight: true,
							$outerWidth: true,
							$pageXOffset: true,
							$pageYOffset: true,
							$parent: true,
							$scrollLeft: true,
							$scrollTop: true,
							$scrollX: true,
							$scrollY: true,
							$self: true,
							$webkitIndexedDB: true,
							$webkitStorageInfo: true,
							$window: true
						};
						var hasAutomationEqualityBug = (function () {
							/* global window */
							if (typeof window === 'undefined') {
								return false;
							}
							for (var k in window) {
								try {
									if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
										try {
											equalsConstructorPrototype(window[k]);
										} catch (e) {
											return true;
										}
									}
								} catch (e) {
									return true;
								}
							}
							return false;
						}());
						var equalsConstructorPrototypeIfNotBuggy = function (o) {
							/* global window */
							if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
								return equalsConstructorPrototype(o);
							}
							try {
								return equalsConstructorPrototype(o);
							} catch (e) {
								return false;
							}
						};

						function isArgumentsObject(value) {
							var str = toStr.call(value);
							var isArgs = str === '[object Arguments]';
							if (!isArgs) {
								isArgs = str !== '[object Array]' &&
									value !== null &&
									typeof value === 'object' &&
									typeof value.length === 'number' &&
									value.length >= 0 &&
									toStr.call(value.callee) === '[object Function]';
							}
							return isArgs;
						};

						return function keys(object) {
							var isFunction = toStr.call(object) === '[object Function]';
							var isArguments = isArgumentsObject(object);
							var isString = toStr.call(object) === '[object String]';
							var theKeys = [];

							if (object === undefined || object === null) {
								throw new TypeError('Cannot convert undefined or null to object');
							}

							var skipProto = hasProtoEnumBug && isFunction;
							if (isString && object.length > 0 && !has.call(object, 0)) {
								for (var i = 0; i < object.length; ++i) {
									theKeys.push(String(i));
								}
							}

							if (isArguments && object.length > 0) {
								for (var j = 0; j < object.length; ++j) {
									theKeys.push(String(j));
								}
							} else {
								for (var name in object) {
									if (!(skipProto && name === 'prototype') && has.call(object, name)) {
										theKeys.push(String(name));
									}
								}
							}

							if (hasDontEnumBug) {
								var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);

								for (var k = 0; k < dontEnums.length; ++k) {
									if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
										theKeys.push(dontEnums[k]);
									}
								}
							}
							return theKeys;
						};
					}());
				}
				//为IE浏览器补丁end===============================

				//其余的扩展here
				//如果还没有扩展这个语言级工具类,则扩展之
				if (!Date.prototype.format) {
					//对Date的扩展,将 Date 转化为指定格式的String 
					//月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 
					//年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
					//例子: 
					//(new Date()).format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
					//(new Date()).format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18 
					Date.prototype.format = function (fmt) { // author: meizz
						var o = {
							"M+": this.getMonth() + 1, // 月份
							"d+": this.getDate(), // 日
							"h+": this.getHours(), // 小时
							"m+": this.getMinutes(), // 分
							"s+": this.getSeconds(), // 秒
							"q+": Math.floor((this.getMonth() + 3) / 3), // 季度
							"S": this.getMilliseconds()
							// 毫秒
						};
						if (/(y+)/.test(fmt))
							fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "")
								.substr(4 - RegExp.$1.length));
						for (var k in o)
							if (new RegExp("(" + k + ")").test(fmt))
								fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) :
									(("00" + o[k]).substr(("" + o[k]).length)));
						return fmt;
					};
				}
			}

			//TODO: 其它语言扩展可添加于此
		},

		//构造函数表
		_classes: {
			/**
			 * 发布、订阅模式的工厂类<br>
			 * 如果想用,请实例化,例如:var pubSub = new NS.PubSub();<br>
			 * 绑定事件的方法:bind(eventType, handler)、on(eventType, handler)<br>
			 * 解除事件的方法:clear(eventType, handler)、off(eventType,
			 * handler),可以解除绑定某个事件,也可以解除绑定所有事件<br>
			 * 查看已绑事件列表:show(eventType)、view(eventType)<br>
			 * 手动触发某个事件:fire(eventType)、trigger(eventType)、emit(eventType)<br>
			 * 重置该对象,移出所有绑定的事件; reset()
			 */
			PubSub: function () {
				//是否开启日志
				this.debugMode = true;
				/**
				 * 事件处理器列表
				 * 结构如下:
				 * 	click:[functionA, functionB, functionC, ...]
				 *  blur:[functionD, functionE, functionF, ...]
				 *  ...
				 */
				this.handlers = {};
				/**
				 * 为某种事件名成绑定(追加)一个新的事件监听器<br>
				 * 例如,PubSub.on("click",function(){ doSth...})
				 */
				this.bind = this.on = function (eventType, handler) {
					if (!(eventType in this.handlers)) {
						// 若还从未绑定过此种事件类型,例如click事件
						this.handlers[eventType] = []; // 为该种类的事件类型初始化一个空的处理器数组
					}
					// 此时,this.handlers[eventType]可能是一个空数组,也可能是一个已经有了若干个处理器的数组了!
					this.handlers[eventType].push(handler);
					if (this.debugMode) {
						console.log("成功绑定" + eventType + "事件!");
					}
					// 返回this,便于后续的继续的链式调用
					return this;
				};
				/**
				 * 移除事件处理器
				 */
				this.clear = this.off = function (eventType, handler) {
					if (eventType) {
						//对指定的事件类型进行移除
						if (handler) {
							// 如果传参了,那么只移除指定的handler
							this.handlers[eventType].pop(handler);
							if (this.debugMode) {
								console.log("成功解除指定参数类型的" + eventType + "事件!");
							}
						} else {
							// 移除所有的handler
							this.handlers[eventType] = [];
							if (this.debugMode) {
								console.log("成功解除所有的" + eventType + "事件!");
							}
						}
					} else {
						//移除所有的事件类型
						for (var et in this.handlers) {
							if (typeof (et) == "function") {
								//是一个function,不可以删除 
							} else if (typeof (et) == "string") {
								//可以删除
								delete this.handlers[et];
							}
						}
					}
					return this;
				};
				/**
				 * 打印所有的事件列表
				 */
				this.show = this.view = function (eventType) {
					if (eventType) {
						if (this.handlers[eventType]) {
							console.log("当前绑定" + eventType + "类型的事件回调函数列表为:\n" + this.handlers[eventType]);
						}
					} else {
						for (var p in this.handlers) {
							if (typeof (p) == "string") {
								console.log("当前绑定" + p + "类型的事件回调函数列表为:\n" + this.handlers[p]);
							}
						}
					}
					return this;
				};
				/**
				 * 手工触发某个事件<br>
				 * 例如:PubSub.emit("click","param1","param2");<br>
				 * 那么,就会将PubSub对象上面绑定的所有的click事件函数依次触发(调用)一遍
				 */
				this.fire = this.trigger = this.emit = function (eventType) {
					// 从第一个参数往后截取所有的后续参数的数组,例如,此例中,就是["param1","param2"]
					var handlerArgs = Array.prototype.slice.call(arguments, 1);
					if (this.handlers[eventType] && this.handlers[eventType].length > 0) {
						for (var i = 0; i < this.handlers[eventType].length; i++) {
							// this.handlers[eventType][i]就是那个绑定的函数,调用该函数,并传入参数,即可
							this.handlers[eventType][i].apply(this, handlerArgs);
						}
					} else {
						if (this.debugMode) {
							console.log("未绑定任何 【" + eventType + "】 事件!忽略调用!");
						}
					}
					// 返回this,便于后续的继续的链式调用
					return this;
				};
				/**
				 * 重置整个对象,清除其上面绑定的所有的事件<br>
				 * 该方法用于重新使用该对象
				 */
				this.reset = function () {
					this.handlers = {};
					console.log("对象已重置,绑定的事件已被清空!");
				}
			}

			//TODO: 其它构造函数可添加于此
		}
	};

	//让JS扩展即刻生效
	NS._ext.jsLangExt();

	//调用构造函数
	//创建一个开箱即用的发布订阅模式的实例
	NS.PB = new NS._classes.PubSub();

	window.NS = NS;
}(window));