JavaScript02: 进阶
一. 变量声明
1.1 变量提升
// 以下代码,或多或少会有些问题的
function fn(){console.log(name);var name = '大马猴';
}fn()// 问题:
name变量先使用,再定义 这么写代码,在其他语言里. 绝对是不允许的
但是在js里,不但允许,还能执行,为什么呢?
因为在js执行的时候,它会首先检测整体代码,发现在代码中会有name使用
则运行时,就会自动变成这样的逻辑:// 变量提升的逻辑:
function fn(){var name;console.log(name);name = '大马猴';
}
fn()
console.log(a); // undefined// 看到了么,实际运行的时候和写代码的顺序可能会不一样
这种 把变量 提升到代码块第一部分运行 的逻辑,被称为变量提升
1.2 let 声明变量
结论一:用let声明变量,是新版本javascript ES6 提倡的一种声明变量的方案
// ES6提出 用let 声明变量,防止变量提升的逻辑function fn(){console.log(name); // 直接报错, let变量不可以变量提升let name = '大马猴';
}
fn()
结论二:在同一个作用域内. let声明的变量只能声明一次,其他使用上和var没有差别
function fn(){// console.log(name); // 直接报错, let变量不可以变量提升.// let name = '大马猴';var name = "周杰伦";var name = "王力宏"; // 不会报错console.log(name);
}fn()// var本意是声明变量,同一个变量,被声明两次(都是函数体内部,局部变量),显然也是不合理的
ES6规定:let声明的变量,在同一个作用域内,只能声明一次
function fn(){// console.log(name); // 直接报错, let变量不可以变量提升.// let name = '大马猴';let name = "周杰伦";console.log(name);let name = "王力宏"; // 报错,同一个作用域 let 声明同一个变量,只能一次console.log(name);
}
fn()// 注意: 报错是发生在代码检查阶段. 所以上述代码,根本就执行不了
二. 闭包函数
先看一段代码
let name = "周杰伦"
function chi(){name = "吃掉"
}
chi();
console.log(name); // "吃掉"// 发现没有: 在函数内部修改,外部的变量是十分容易的一件事. 尤其是全局变量. 这是非常危险的.
// 试想 写了一个函数. 要用到name, 结果被别人写的某个函数给修改掉了...
接下来,看一个案例:
准备两个工具人. 来编写代码. 分别是js01和js02.
// 1号工具人
var name = "alex"// 定时器,5000秒后执行
setTimeout(function(){console.log("一号工具人:" + name) // 一号工具人还以为是alex呢, 但是该变量是不安全的.
}, 5000);
// 2号工具人
var name = "周杰伦"
console.log("二号工具人", name);
html:
<script src="js01.js"></script><script src="js02.js"></script>
此时运行的结果:
很明显, 虽然各自js在编写时是分开的. 但是在运行时, 是在同一个空间内执行的. 他们拥有相同的作用域
此时的变量势必是非常非常不安全的. 那么如何来解决呢?
注意:在js里 变量是有作用域的. 也就是说一个变量的声明和使用是有范围的,不是无限的
// 验证: 变量是有作用域的
function fn(){let love = "爱呀"
}
fn()console.log(love)// 直接就报错了 也就是说. 在js里 变量作用域是有全局和局部 的概念
直接声明在最外层的变量,就是全局变量。所有函数、所有代码块都可以共享的
在函数内和代码块内声明的变量,尤其是函数内,声明出来的变量它是一个局部变量,外界是无法进行访问的
我们就可以利用这一点,来给每个工具人创建一个局部空间. 就像这样:
// 1号工具人 都是自运行函数
(function(){var name = "alex";setTimeout(function(){console.log("一号工具人:"+name)}, 5000);
})()
// 二号工具人 都是自运行函数
!function(){var name = "周杰伦"console.log("二号工具人", name);
}()
运行结果
这样,虽然解决了变量的冲突问题
但是想想. 如果在外面,需要函数内部的一些东西,来进行相关操作,怎么办?
比如 一号工具人要提供一个功能(加密),外界要调用, 怎么办?
// 1号工具人// 局部函数中,对外接口 方式一: return 返回到 全局变量中 ===> 闭包函数
// 1.首先:全局要使用,那js文件中,就不能是自运行函数,要设置一个名字
let jiami = (function(){let key = "10086" // 假装我是秘钥// 我是一个加密函数let mi = function(data){ // 数据console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+key);console.log("数据:"+data);// 返回密文return "我要返回密文";}// 2.其次:外面可能需要用到该功能. 故 需要该变量返回(暴露到全局空间). 返回加密函数return mi;
})();// 局部函数中,对外接口 方式二:借助于window对象,将返回的变量 直接 赋值到全局变量中
(function(){let key = "10086" // 假装我是秘钥// 我是一个加密函数let mi = function(data){ // 数据console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+key);console.log("数据:"+data);// 返回密文return "我要返回密文";}// 对外的接口window.mi = mi;
})();
注意:如果封装一个加密js包的时候,就还得准备出解密的功能
并且, 不可能一个js包就一个功能吧。 那也太痛苦了(js文件起名字),那怎么办?
可以返回一个对象,对象里面可以存放好多个功能
而一些不希望外界触碰的功能. 就可以很好的保护起来.
// 1号工具人.
let jiami = (function(){let key = "10086" // 加装我是秘钥// 该函数只属于该模块内部,外界无法访问. 就不返回let n = {abc:function(){console.log("我是abc. 你叫我干什么?")}}// 外面需要用到的功能,就进行返回.return {rsa_jiami: function(data){console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+this.get_rsa_key() + key);n.abc();console.log("数据:"+data);return "我要返回密文";},aes_jiami: function(data){console.log("接下来, 我要加密了,aes哦. 很厉害的")console.log("秘钥:"+this.get_aes_key());n.abc();console.log("秘钥:"+key);console.log("数据:"+data);return "我要返回密文";},get_rsa_key: function() {return this.rsa_key = "rsa的key", this.rsa_key},get_aes_key: function() {return this.rsa_key = "aes的key", this.rsa_key}}
})();
html里面使用时:
<script>miwen = jiami.rsa_jiami("吃你的糖葫芦吧");console.log(miwen);
</script>
OK. 至此. 何为闭包? 上面这个就是闭包
相信你百度一下就会知道,什么内层函数使用外层函数变量、什么让一个变量常驻内存等等
其实细看,它之所以称之为闭包,它是一个封闭的环境。在内部. 自己和自己玩儿
避免了对该模块内部的冲击和改动. 避免的变量之间的冲突问题
闭包的特点:
- 内层函数对外层函数变量的使用
- 会让变量常驻于内存
这俩玩意就不解释了, 和python的闭包是一个意思。不懂没关系,能看懂它的执行过程就好
三. JS中的各种操作(非交互)
3.1 定时器
在JS中, 有两种设置定时器的方案
// 延时器:经过xxx时间后, 执行xxx函数
t = setTimeout(函数, 时间)// 5000毫秒 5秒后打印我爱你
t = setTimeout(function(){console.log("我爱你")
}, 5000); clearTimeout(t) // 停止一个定时器
// window.clearTimeout(t)
// 定时器:每隔 xxx时间, 执行一次xxx函数
t = setInterval(函数, 时间) // 每隔5秒钟, 打印`我爱你`
t = setInterval(function(){console.log("我爱你")
}, 5000)window.clearInterval(t) // 停止一个定时器for(let i = 0; i <= 9999; i++)window.clearInterval(i); // 清理掉所有定时器
// 定时器 关于js逆向,常遇到的:// 1.心跳检测 一般是用来监测用户 是否掉线了,也可以用来监测用户浏览器环境是否健康
// 这个就是 服务端 主动向 浏览器端 发送请求检测
http是被动响应的 服务器端只能不停的间隔发送检测请求,才能时刻监测客户端某个元素的状态(是否点击 或 扫描二维码)等js逆向中:心跳检测
就是正常浏览器是 不停的心跳检测 且有数据返回,
js逆向代码时,可能只发一次,服务端就给做反爬 禁IP什么的// 2.无限debugger
// 无限debugger的核心
setInterval(function(){debugger; // 设置断点
}, 1000)网页页面中,会正常显示其他的html代码,但是一旦F12调试,就会一直处理断点中// 解决原理:
在source源代码中,setInderval 这一行(没进入定时器体内之前),左键点击行号(设置断点)刷新页面,页面会调试暂停到 设置断点这一行再在控制台(console)中,将定时器干掉 (重置定时器为普通的空函数 setInterval=function(){}; )
3.2 关于时间
eg:http://www.baidu.com/s?word=jfdsaf&t=1640090719637 参数t就是时间戳
var d = new Date(); // 获取当前系统时间
var d = new Date("2018-12-01 15:32:48"); // YYYY-MM-DD HH:mm:ss得到一个具体时间// 时间格式化
year = d.getFullYear(); // 年份
month = d.getMonth() + 1; // 月份. 注意月份从0开始
date = d.getDate(); // 日期
hour = d.getHours(); // 小时
minute = d.getMinutes(); // 分钟
seconds = d.getSeconds(); // 秒format_date = year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + seconds;// 时间戳 表示从1970-1-1 00:00:00 到现在一共经过了多少毫秒
var d = new Date();
console.log(d.getTime())// 注意1:python的时间戳 单位是秒
import time
print( int(time.time() * 1000) ) # 扩大一千倍,再取整// 注意2: 有些时候,前端实例化日期对象时,会少一个调用括号
var d = new Date; // 坑人写法,也可以
console.log(d.getTime())
3.3 eval函数(必须会)
http://tools.jb51.net/password/evalencode 一个在线JS处理eval的网站. 大多数的eval加密. 都可以搞定了.
// eval:
可以动态的把字符串,当成js代码运行 // 从功能上讲非常简单,和python里面的eval是一样的s = "console.log('我爱你')";
eval(s);// 重点:eval加密
拓展使用 --> 前端利用eval的特性来完成反爬操作// 解决核心:
eval函数,里面传递的应该是 即将要执行的 代码(字符串)// eg:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0.1(\'我爱你\')',62,2,'console|log'.split('|'),0,{}))// 记住eval()里面是字符串 !!!
这一坨看起来, 肯定很不爽. 怎么变成看着很舒服的样子呢? 想看看这个字符串长什么样?
就把eval()里面的东西拷贝出来
执行一下 // 注意:一般是个自运行函数,直接粘贴会报错 故加个括号 eg: (eval中的代码) // 或 和下面截图一样 赋值给变量,再打印变量
最终一定会得到一个字符串,要不然eval()执行不了的
3.4 prototype 原型对象
prototype 原型对象,作用是 给类增加功能扩展 的一种模式
写个面向对象来看看.
function People(name, age){this.name = name;this.age = age;this.run = function(){console.log(this.name+"在跑")}
}p1 = new People("张三", 18);
p2 = new People("李四", 19);p1.run();
p2.run();
现在代码写完了. 突然之间, 感觉好像少了个功能. 人不应该就一个功能. 光会吃是不够的. 还得能够ooxx. 怎么办?
直接改代码? 可以,但不够好. 如果这个类不是我写的呢? 随便改别人代码是很不礼貌的,也很容易出错. 怎么办?
可以在我们自己代码中,对某个类型动态增加功能。此时就用到了prototype
function People(name, age){this.name = name;this.age = age;this.run = function(){console.log(this.name+"在跑")}
}// 通过prototype,可以给People增加功能 属性或方法
People.prototype.xxoo = function(){console.log(this.name+"还可以xxoo");
}p1 = new People("张三", 18);
p2 = new People("李四", 19);p1.run();
p2.run();p1.xxoo();
p2.xxoo();
几个重要概念
3.4.1 构造器
构造一个对象的函数. 叫构造器.
function People(){ // People 就是构造器 constractor}var p = new People(); // 调用构造器 ---> 对象p.constractor == People; // true
3.4.2 原型对象
每一个js对象中,都有一个隐藏属性__proto__
,指向该对象的 原型对象
在执行该对象的方法或者查找属性时,首先, 对象自己(构造器中声明的)是否存在该属性或者方法
如果存在, 就执行自己的. 如果自己不存在. 就去找 原型对象
function Friend(){this.chi = function(){console.log("我的朋友在吃");}
};// 指定Friend 的原型对象
Friend.prototype = {chi: function(){console.log("我的原型在吃")}
};// 或者这种写法
Friend.prototype.chi = function(){console.log("我的原型在吃")
};f = new Friend();
f.chi(); // 运行结果: 我的朋友在吃// 属性查找顺序:
先查找该对象(构造器中声明的)中 是否有chi这个方法
再找,它的原型对象上 是否有chi这个方法// 总结: !!!
Friend // 构造器f // 对象f.__proto__ <===> Friend.prototype // 构造器的prototype属性 和 对象的 __proto__,都是指向f对象 的 原型对象
3.4.3 原型链
原型链(prototype chain):是属性查找的方式
当调用一个对象的属性时,如果对象没有该属性,从对象的原型对象上去找该属性,
如果原型上也没有该属性,那就去找原型的原型,直到最后返回null为止,null没有原型。
// 前提:
每个对象身体里. 都隐藏着 __proto__属性 也就是它的 原型对象
同时 原型对象 也是对象, 也就是说 原型对象 也有 __proto__ 属性类似于.....这样:f.__proto__.__proto__ ===> Friend.prototype.__proto__ ===> Object.prototype // Object对象的原型
打印出来的效果是这样的:
// 故:在执行 f.toString() 的时候不会报错. 可以正常运行的原因,就在这里执行过程:
先找 f对象 中是否有 toString 没有
找它的 原型对象,原型对象 中没有
继续找 原型对象的原型对象
直至找到Object的原型为止,如果还没有,就报错了. f.hahahahahahah() // 报错 // 综上:
原型链是js 方法查找的路径指示标
3.4.4 原型链的延伸使用
用原型链能做什么? 网站反爬(恶心): 有些页面网站,通过原型链,让你F12时,一直无限debug
看一段神奇的代码
(function(){debugger})(); // 这样一段代码可以看到. 浏览器进入了debugger断点. // 这段代码的背后是什么呢? // 注意:
在js代码执行时,每一个function的对象,都是通过Function()来创建的
也就是说 函数是Function()的对象// 校验:
function fn(){}
console.log(fn.__proto__.constructor); // fn函数的原型 构造器是 ƒ Function() { [native code] }// 所以:函数就是Function的对象. 那么,我们也可以通过Function来构建一个函数. new Function('debugger')(); // 效果一样的.
OK. 这东西对我们来说有什么用. 上代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="haha.js"></script><script>txsdefwsw();</script>
</head>
<body>有内鬼. 终止交易
</body>
</html>
haha.js 中的内容如下:
function txsdefwsw() {var r = "V", n = "5", e = "8";function o(r) {if (!r) return "";for (var t = "", n = 44106, e = 0; e < r.length; e++) {var o = r.charCodeAt(e) ^ n;n = n * e % 256 + 2333, t += String.fromCharCode(o)}return t}try {var a = ["r", o("갯"), "g", o("갭"), function (t) {if (!t) return "";for (var o = "", a = r + n + e + "7", c = 45860, f = 0; f < t.length; f++) {var i = t.charCodeAt(f);c = (c + 1) % a.length, i ^= a.charCodeAt(c), o += String.fromCharCode(i)}return o}("@"), "b", "e", "d"].reverse().join("");!function c(r) {(1 !== ("" + r / r).length || 0 === r) && function () {}.constructor(a)(), c(++r)}(0)} catch (a) {setTimeout(txsdefwsw, 100);}
}
结果:页面跑起来没什么问题. 但是会无限debugger;
解决方案:
-
找到断点出. 右键 -> never pause here
-
写js hook代码
var xxxx = Function.prototype.constructor; Function.prototype.constructor = function(code){console.log("i love you");if (code != 'debugger'){return new xxxx(code);} else {return;} }
更加详细的hook. 下节课会讲.
3.5 window对象
window对象是一个很神奇的东西,可以理解成javascript的全局,是整个浏览器的全局作用域
如果默认不用任何东西,访问一个标识符,那么默认是在用window对象
例如:
eval === window.eval // true
setInterval === window.setInterval // truevar a = 10;
a === window.a // truefunction fn(){}
fn === window.fn // truewindow.mm = "爱你"console.log(mm); //"爱你"// window 中有很多功能对象,还可以控制页面跳转window.location.herf = "新地址" // 当前窗口 跳转到新地址url
综上:全局变量可以用window.xxx来表示
ok. 接下来注意看. 我要搞事情了
(function(){let chi = function(){console.log("我是吃")}window.chi = chi
})();chi()// 换一种写法. 你还认识么?
(function(w){let chi = function(){console.log("我是吃")}w.chi = chi
})(window);// 再复杂一点
(function(w){let tools = {b64: function(){console.log("我是计算B64");return "b64";},md5: function(){console.log("我是计算MD5");return "MD5"}}w.jiami = {AES: function(msg){return tools.b64(),tools.md5(),'god like';},DES: function(){console.log("我是DES");},RSA: function(){console.log("我是RSA");}}
})(window);jiami.AES("吃了么");
3.6 call和apply
对于逆向工程师而言,并不需要深入的理解call和apply的本质作用.,只需要知道这玩意执行起来的逻辑顺序是什么即可
在运行时,正常的js调用:
function People(name, age){this.name = name;this.age = age;this.chi = function(){console.log(this.name, "在吃东西")}
}p1 = new People("alex", 18);
p2 = new People("wusir", 20);p1.chi();
p2.chi();
接下来,可以使用call和apply也完成同样的函数调用
function People(name, age){this.name = name;this.age = age;this.chi = function(what_1, what_2){console.log(this.name, "在吃", what_1, what_2);}
}p1 = new People("alex", 18);
p2 = new People("wusir", 20);p1.chi("馒头", "大饼");
p2.chi("大米饭", "金坷垃");function eat(what_1, what_2){console.log(this.name, "在吃", what_1, what_2);
}// call的语法是: 函数.call(对象, 参数1, 参数2, 参数3....)
// 执行逻辑是: 执行函数. 并把对象传递给函数中的this. 其他参数照常传递给函数
eat.call(p1, "查克拉", "元宇宙");
apply和他几乎一模一样. 区别是: apply传递参数要求是一个数组
eat.apply(p1, ["苞米茬子", "大饼子"]);
3.7 ES6中的箭头函数
在ES6中简化了函数的声明语法.
var fn = function(){};
var fn = () => {};var fn = function(name){}
var fn = name => {}
var fn = (name) => {}var fn = function(name, age){}
var fn = (name, age) => {}
3.8 ES6中的promise(难)
具体执行过程和推理过程. 请看视频. 这里很饶腾.
function send(url){return new Promise(function(resolve, reject){console.log("我要发送ajax了", url)setTimeout(function(){console.log("我发送ajax回来了")// 成功了, 要去处理返回值resolve("数据", url);}, 3000);});
}send("www.baidu.com").then(function(data){console.log("我要处理数据了啊", data);return send("www.google.com");
}).then(function(data, url){console.log("我又来处理数据了", data);
});
3.9 逗号运算符
function s(){console.log(1), console.log(2), console.log(3); // 从前向后执行 ,1,2,3let s = (1, 2, 3); // 整体进行赋值的时候. 取的是最后一个值 3console.log(s);// 注意. 这个括号可以在返回值时省略var a;return a=10,a++,a+=100,{name:"alex", "a":a};
}
let r = s();
console.log(r); // {name: 'alex', a: 111}
3.10 三元运算符
// 三元运算符
条件?值1:值2 // 条件成立时,返回 ?后面的 反之,返回 :后面的let a = 10;
let b = 20;
let d = a > b? a: b ;
console.log(d); // 20 看一个恶心的:
let a = 10;
let b = 20;
let d = 17;
let c = 5;let e;
let m;e = (e = a > 3 ? b : c, m = e < b++ ? c-- : a = 3 > b % d ? 27: 37, m++)
console.log(e);
console.log(c);
console.log(m);
3.11 JS hook
hook又称钩子,可以在调用系统函数之前,先执行我们的函数. 钩子函数
例如:hook eval
eval_ = eval; // 先保存系统的eval函数
eval = function(s){console.log(s);debugger;return eval_(s);
}
eval()
eval.toString = function(){return 'function eval() { [native code] }'} // 可能会被检测到, 用这种方案来进行
对Function的hook, 主要为了解决无限debugger
fnc_ = Function.prototype.constructor;
Function.prototype.constructor = function(){if(arguments[0]==='debugger'){return;} else {return fnc_.apply(this, arguments);}
}
上面都是hook的系统函数. 但有时,需要hook某个属性. 此时应该怎么办?
var v;
Object.defineProperty(document, "cookie", {set: function(val) {console.log("有人来存cookie了");v = val;debugger;return val;},get() {console.log("有人提取cookie了");debugger;return v;}
});
剩下的,就不再赘述了.
在逆向时, 常用的主要有: hook eval 、hook Function 、hook JSON.stringify、JSON.parse 、hook cookie、hook window对象
四. JS和HTML交互(选修)
在HTML中,可以直接在标签上给出一些事件的触发
例如:页面上的一个按钮
<input type="button" value="点我就爱你"/>
我们能够知道,该标签在页面中会产生一个按钮,但是该按钮无论如何进行点击. 都不会触发任何事件
但此时, 人家其实触发了. 只是你没处理而已. 在点击该按钮的时候. 浏览器其实收集到了点击事件.
但是由于我们没有给出任何 发生了点击事件应该做什么 的事情. 所以也就没有了反应.
可以通过onclick
属性. 来给点击事件添加上具体要做什么
<input type='button' value="点我就爱你" onclick="fn()" />当发生点击事件时去执行fn(). fn() 是什么? fn就是我们javascript的一个函数.
完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script>function fn(){alert("臭不要脸")}</script>
</head>
<body><input type="button" value="点我就爱你" onclick="fn()">
</body>
</html>
至此, 成功实现了 从HTML中调用JS
那么在HTML中有多少种事件可以触发呢? 非常多....记住几个就好了
// html中的事件
click 点击事件
focus 获取焦点
blur 失去焦点
submit 提交表单
change 更换选项
scroll 滚动条滚动
mouseover 鼠标滑过
mouseout 鼠标滑出
mousemove 鼠标滑动
上述是第一种绑定事件的方案. 可以直接在html标签中,使用onxxx
系列属性来完成事件的绑定
同时js,还提供了以下事件绑定方案:
<input type="button" id="btn" value="别点我了"><script>// 注意:必须等到页面加载完毕了. 才可以这样document.querySelector("#btn").addEventListener("click", function(){console.log("你点我干什么?? ")})
</script>
document.querySelector() 给出一个css选择器, 就可以得到一个html页面上标签元素的句柄(控制该标签).
获取句柄的方案有好多. 常见的有:
document.getElementById(); // 根据id的值 获取句柄
document.getElementsByClassName(); // 根据class的值 获取句柄// <form name='myform'><input type="myusername"/></form>
document.form的name.表单元素的name; // document.myform.myusername;
现在相当于,可以从html转到JS中了,并且在js中可以捕获到html中的内容了
此时 对应的表单验证,也可以完成了
<form action="服务器地址" id="login_form"><label for="username">用户名:</label><input type="text" name="username" id="username"><span id="username_info"></span><br/><label for="password">密码:</label><input type="text" name="password" id="password"><span id="password_info"></span><br/><input type="button" id="btn" value="点我登录">
</form>
<script>// 在页面加载的时候window.onload = function(){// let btnEle = document.getElementById('btn')// btnEle.onclick = function(){// 等价于上面document.getElementById('btn').addEventListener("click", function(){// 清空提示信息document.getElementById('username_info').innerText = ""; document.getElementById('password_info').innerText = "";let username = document.getElementById('username').value; // 获取username标签中的value属性let password = document.getElementById('password').value; // 获取密码let flag = true; // 最终是否可以提交表单?if(!username){document.getElementById('username_info').innerText = "用户名不能为空";flag = false;}if(!password){document.getElementById('password_info').innerText = "密码不能为空";flag = false;}if (flag){document.getElementById('login_form').submit();}})}
</script>