From 3ddcfb04a8088481062efc49ec71f9c326de54b0 Mon Sep 17 00:00:00 2001 From: haohu Date: Tue, 6 Feb 2018 14:20:25 +0800 Subject: [PATCH 001/137] tech node --- mypost/tech_note.md | 85 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/mypost/tech_note.md b/mypost/tech_note.md index f16bb687..4df8a782 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -41,6 +41,9 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 - [Web开发技术发展历史](https://www.tianmaying.com/tutorial/web-history) - [Web开发技术的演变](http://blog.jobbole.com/45170/) +经典页面 + +- [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) # 后台 @@ -49,6 +52,10 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 [编程之法:面试和算法心得](https://github.com/julycoding/The-Art-Of-Programming-By-July) + + +InfraredCounterParser InfraredCounterHandler InfraredCounterSender 等字符串,取出最后一个单词,如Parser, Handler, Sender 等 + # 数据库 # 网络 @@ -366,6 +373,10 @@ PATH = %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin - 选区移动:Alt + ↑/↓ - 多选修改:Ctrl + D +### Eclipse + +- 格式化代码:Ctrl + Shift + F + 参考链接: - [为什么我选择使用 VS Code进行前端开发?](https://zhuanlan.zhihu.com/p/28631442) @@ -373,7 +384,7 @@ PATH = %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin # 物联网 -### 参考链接 +## 参考链接 [关于RS232 和 RS485 的区别](http://blog.csdn.net/foreverhuylee/article/details/23375079) @@ -448,7 +459,79 @@ NFC的短距离通信特性正是其优点,由于耗电量低、一次只和 ### PLC +### 继电器 + +继电器是具有隔离功能的自动开关元件,广泛应用于遥控、遥测、通讯、自动控制、机电一体化及电力电子设备中,是最重要的控制元件之一。 + +继电器一般都有能反映一定输入变量(如电流、电压、功率、阻抗、频率、温度、压力、速度、光等)的感应机构(输入部分);有能对被控电路实现“通”、“断”控制的执行机构(输出部分);在继电器的输入部分和输出部分之间,还有对输入量进行耦合隔离,功能处理和对输出部分进行驱动的中间机构(驱动部分)。 + +### 二进制协议 + +二进制常用操作 + +``` + + int value = 170; + void print(int x) => Console.WriteLine(Convert.ToString(x, 2).PadLeft(8, '0')); + int make_mask(int x) => 1 << x; + int set(int x, int i) => x | make_mask(i); + int unset(int x, int i) => x & ~make_mask(i); + bool isset(int x, int i) => (x & make_mask(i)) != 0; + + //170的二进制显示: 10101010 + print(value); + + // 右数第 5 位置 1 : 10111010 + print(set(value, 4)); + + // 右数第 4 位置 0: 10100010 + print(unset(value, 3)); + + // 右数第 4 位是否为1: true + Console.WriteLine(isset(value, 3)); + + //右数第 3 位是否为1: false + Console.WriteLine(isset(value, 2)); +``` + +### 其它 + +TTL电平信号之所以被广泛使用,原因是:通常我们采用二进制来表示数据。而且规定,+5V等价于逻辑“1”,0V等价于逻辑“0”。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。 + +GND是电线接地端的简写。代表地线或0线。这个地并不是真正意义上的地,是出于应用而假设的一个地,对于电源来说,它就是一个电源的负极。 + +VCC:电源电压(双极器件);电源电压(74系列数字电路); + +RXD 为接收数据的引脚,TXD 为发送数据的引脚。 + +DTE提供或接收数据,连接到调制解调器上的计算机就是一种DTE。DTE提供或接收数据,连接到网络中的用户端机器,主要是计算机和终端设备。 + +在网络端的连接设备称为DCE(Data-Communication Equipment)。DTE与进行信令处理的DCE相连。 +DTE通过DCE设备,例如,调制解调器,连接到数据网络。 + +RS232标准中的RTS与CTS:即请求发送/清除发送,用于半双工时的收发切换,属于辅助流控信号。半双工的意思是说,发的时候不收,收的时候不发。那么怎么区分收发呢?缺省时是DCE向DTE发送数据,当DTE决定向DCE发数据时,先有效RTS,表示DTE希望向DCE发送。一般DCE不能马上转换收发状态,DTE就通过监测CTS是否有效来判断可否发送,这样避免了DTE在DCE未准备好时发送所导致的数据丢失。 + +差分输入的是将两个输入端的差值作为信号,这样可以免去一些误差,比如你输入一个1V的信号电源有偏差,比实际输入要大0.1.就可以用差分输入1V和2V一减就把两端共有的那0.1误差剪掉了。单端输入无法去除这类误差。 + +在某些系统里,系统'地'被用作电压基准点。当'地'当作电压测量基准时,这种信号规划被称之为单端的。 + +差分信号的第一个好处是,因为你在控制'基准'电压,所以能够很容易地识别小信号。 +差分信号的第二个主要好处是,它对外部电磁干扰(EMI)是高度免疫的。 +差分信号提供的第三个好处是,在一个单电源系统,能够从容精确地处理'双极'信号。 + +上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用。下拉同理,也是将不确定的信号通过一个电阻钳位在低电平。 + + # 未整理 +[Avoid calling Invoke when the control is disposed](https://stackoverflow.com/questions/1874728/avoid-calling-invoke-when-the-control-is-disposed) + + + +http://blog.csdn.net/pkueecser/article/details/50610796 时间序列数据库的秘密 + + +https://github.com/justjavac/ReplaceGoogleCDN Replace Google CDN +https://stackoverflow.com/questions/31572580/how-covert-c-sharp-datetime-to-java-datetimeusing-joda-time \ No newline at end of file From 28ffd5e4ddd2555ae37c03667a1690312d306783 Mon Sep 17 00:00:00 2001 From: haohu Date: Thu, 8 Feb 2018 17:01:09 +0800 Subject: [PATCH 002/137] ps --- mypost/tech_note.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 4df8a782..acf3a0a9 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -45,6 +45,40 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 - [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) +## 切图 + +- 取色:可以用 FSCapture 来取 +- 字号大小:PS里一般用 pt 单位,要转换成网页上的px +- 字体设置: font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; +- 测量间距 + - "编辑"-"首选项"-"单位与标尺", 把单位设置为像素 px + - "视图"-"标尺"(Ctrl+R) 把标尺显示出来 + - 按住 Shift 键盘,从标尺拖出参考线,会自动对齐物体的边缘 + - 工具栏中选择标尺工具,按住 Shift 键(会自动垂直或水平),先点击第一条辅助线,再拖动到第二条辅助线,然后看信息栏里的 W 和 H,表示宽和高的信息 + - 量完尺寸后可以"视图"-"清除参考线" +- 矢量 Logo 导出: + - 通过图层的隐藏显示,找到 Logo 对应的矢量智能对象, + - 右键点击图层,转换为智能对象 + - 按住 Ctrl 单击智能对象图层,再 Ctrl + C 复制选中的内容 + - Ctrl + N 新建文件,图像宽高会根据复制内容大小自动选定,背景内容选择 "透明" + - Ctrl + V 粘贴内容,"文件"-"存储为 Web 所用格式",格式选择 PNG-24, 存储即可,如选 PNG-8 可能会有毛刺 +- 图片导出: + - 打开移动工具,选项里把"自动选择"的勾打上, + - 点击要切割的图片,图层面板一般会自动选择图片所在的图层,即使定位不到具体图层,也能定位到图层组 + - 如果该图片是组合图形,或者有图层效果,或者有一个背景图一个遮罩,则按住 Ctrl 选中多个图层,右键合并图层 + - 按住 Ctrl 单击图层,Ctrl + C, Ctrl + N, Ctrl + V, Ctrl + Shif + Alt + S + + +字体大小的设置单位,常用的有2种:px、pt。这两个有什么区别呢? +先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点; +pt就是point,是印刷行业常用单位,等于1/72英寸。 + +参考链接: + +- [PT与PX区别](https://www.douban.com/note/155032221/) +- [ps标尺和参考线知识点及快捷键](http://www.ittribalwo.com/article/1625.html) +- [CSS布局——左定宽度右自适应宽度并且等高布局](https://www.w3cplus.com/css/two-cloumn-width-one-fixed-width-one-fluid-width) + # 后台 From d4688efc3eccf1d6865c0c5b3ca0283847356278 Mon Sep 17 00:00:00 2001 From: haohu Date: Sat, 24 Feb 2018 16:37:38 +0800 Subject: [PATCH 003/137] fix --- javascript/curry.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/javascript/curry.html b/javascript/curry.html index 71f596b7..faddb418 100644 --- a/javascript/curry.html +++ b/javascript/curry.html @@ -1,20 +1,23 @@  + + indexOf实现 + - + \ No newline at end of file From 4671e1d74a372d05bd31b9d2660edcae27d74418 Mon Sep 17 00:00:00 2001 From: haohu Date: Sat, 24 Feb 2018 16:40:39 +0800 Subject: [PATCH 004/137] go --- mypost/tech_note.md | 110 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/mypost/tech_note.md b/mypost/tech_note.md index acf3a0a9..8712245e 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -41,9 +41,6 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 - [Web开发技术发展历史](https://www.tianmaying.com/tutorial/web-history) - [Web开发技术的演变](http://blog.jobbole.com/45170/) -经典页面 - -- [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) ## 切图 @@ -73,11 +70,60 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点; pt就是point,是印刷行业常用单位,等于1/72英寸。 + 参考链接: - [PT与PX区别](https://www.douban.com/note/155032221/) - [ps标尺和参考线知识点及快捷键](http://www.ittribalwo.com/article/1625.html) + + +## 经典页面 + +- [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) +- http://www.raiseai.com/ + +## 常见任务 + +### 左右等高 + +- [4 Methods For Creating Equal Height Columns In CSS](http://vanseodesign.com/css/equal-height-columns/) +- [Fluid Width Equal Height Columns](https://css-tricks.com/fluid-width-equal-height-columns/) - [CSS布局——左定宽度右自适应宽度并且等高布局](https://www.w3cplus.com/css/two-cloumn-width-one-fixed-width-one-fluid-width) +- [Equal Height Column Layouts with Borders and Negative](https://www.smashingmagazine.com/2010/11/equal-height-columns-using-borders-and-negative-margins-with-css/) + + +### 背景图全屏 + +``` +html,body{ + width:100%; + height:100% +} + +body{ + width: 100%; + height:auto; + background:#343434 url("http://github.com/assets/img/bg.jpg") no-repeat; + background-size: cover; +} +``` + +## 参考链接 + +- [CSS Stacking Context里那些鲜为人知的坑](http://blog.angular.in/css-stacking-contextli-na-xie-xian-wei-ren-zhi-de-keng/) +- [HTML 和 Body 在 CSS 中的区别](https://csspod.com/html-vs-body-in-css/) +- [等宽列背后的表格布局算法](https://csspod.com/table-width-algorithms/) +- [Appendix D. Default style sheet for HTML 4](https://www.w3.org/TR/CSS2/sample.html) +- [学习CSS布局](https://www.w3cplus.com/css/learn-css-layout.html) +- [w3school HTML 系列教程](http://www.w3school.com.cn/h.asp) +- [CSS参考手册](http://www.css88.com/book/css/) +- [10 个最常见的 JavaScript 错误(以及如何避免它们)](http://www.css88.com/archives/9184) + +## todo + +- rem + + # 后台 @@ -90,12 +136,15 @@ pt就是point,是印刷行业常用单位,等于1/72英寸。 InfraredCounterParser InfraredCounterHandler InfraredCounterSender 等字符串,取出最后一个单词,如Parser, Handler, Sender 等 +中英文混排,如何在英文和数字两边增加空格 + # 数据库 # 网络 # Java -[Google Guava官方教程(中文版)](http://ifeve.com/google-guava/) +- [Google Guava官方教程(中文版)](http://ifeve.com/google-guava/) +- [Windows7下Maven环境搭建及其使用](http://blog.csdn.net/xuexiaoxu1990/article/details/52882664) ## 环境变量配置 @@ -372,7 +421,9 @@ PATH = %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin ## 其它 -[f.lux - 全天候保护眼睛健康软件!自动调整屏幕色温减少蓝光防疲劳,长时间玩电脑必备!](https://www.iplaysoft.com/flux.html) +- [f.lux - 全天候保护眼睛健康软件!自动调整屏幕色温减少蓝光防疲劳,长时间玩电脑必备!](https://www.iplaysoft.com/flux.html) +- [MarkDown 写 ppt](https://yhatt.github.io/marp/) +- [在线根据 markdown 生成 ppt](http://www.vmfor.com/ppt/index.html) ## 编辑器 @@ -555,6 +606,48 @@ RS232标准中的RTS与CTS:即请求发送/清除发送,用于半双工时 上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用。下拉同理,也是将不确定的信号通过一个电阻钳位在低电平。 +# 企业信息化 + +大多数OA产品功能集中在信息共享、行政办公领域,一些主流OA系统虽然引入了工作流,但相对比较封闭,开放性和扩展性不够。 + +BPM是一个开放性平台,不仅能实现所有OA的功能,还能满足企业内部系统之间集成的需求,在BPM引擎驱动下,企业的流程终会形成一个闭环。 + +ERP + +WMS + +MES + +ESB + +SOA + +- [智能MES解决方案](http://www.rtdsoft.com/channels/57.html) +- [制造执行系统(MES)选型与实施指南简版](https://wenku.baidu.com/view/052b5ef4a32d7375a41780c8.html) +- [OpenMES架构说明书](https://wenku.baidu.com/view/2a98711ec281e53a5802ffc8.html) + +## 快速开发框架 + +- 登录注册: Apache Shiro +- 组织机构 +- 权限管理: Apache Shiro +- 增删改查 +- 后台界面 +- 菜单管理 +- 工作流:Activity +- 报表:JasperReports + +参考 + +- http://www.jeecg.org/ +- [Java通用权限系统管理(Spring+springMVC+ibatis+Angularjs)](http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/liaodehong/article/details/53100313) +- [组织机构对象模型设计及实现](http://blog.csdn.net/wangpeng047/article/details/7280800) +- [LigerUI 快速开发UI框架](http://www.ligerui.com/) +- https://github.com/thinkgem/jeesite + +jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块 +http://blog.csdn.net/qing_gee/article/details/76223064 + # 未整理 @@ -568,4 +661,9 @@ http://blog.csdn.net/pkueecser/article/details/50610796 时间序列数据库的 https://github.com/justjavac/ReplaceGoogleCDN Replace Google CDN -https://stackoverflow.com/questions/31572580/how-covert-c-sharp-datetime-to-java-datetimeusing-joda-time \ No newline at end of file +https://stackoverflow.com/questions/31572580/how-covert-c-sharp-datetime-to-java-datetimeusing-joda-time +https://pine.fm/LearnToProgram/ +http://www.qdaily.com/articles/42060.html +https://zh.wikihow.com/%E5%AD%A6%E4%B9%A0%E7%BC%96%E7%A8%8B + +nginx waf \ No newline at end of file From 5994efa51ada72ecba11624845518ffef25174a7 Mon Sep 17 00:00:00 2001 From: haohu Date: Mon, 7 May 2018 18:13:53 +0800 Subject: [PATCH 005/137] go --- mypost/tech_note.md | 272 ++++++++++++++++++++++- php/wawa framework/Readme.md | 9 + php/wawa framework/config.php | 11 + php/wawa framework/controllers/index.php | 40 ++++ php/wawa framework/public/index.php | 4 + php/wawa framework/public/test.html | 1 + php/wawa framework/views/index.php | 23 ++ php/wawa framework/views/user_delete.php | 18 ++ php/wawa framework/views/user_form.php | 26 +++ php/wawa framework/wawa.php | 139 ++++++++++++ 10 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 php/wawa framework/Readme.md create mode 100644 php/wawa framework/config.php create mode 100644 php/wawa framework/controllers/index.php create mode 100644 php/wawa framework/public/index.php create mode 100644 php/wawa framework/public/test.html create mode 100644 php/wawa framework/views/index.php create mode 100644 php/wawa framework/views/user_delete.php create mode 100644 php/wawa framework/views/user_form.php create mode 100644 php/wawa framework/wawa.php diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 8712245e..c88668c7 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -70,6 +70,8 @@ Web设计初衷是一个静态信息资源发布媒介,通过超文本标记 先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点; pt就是point,是印刷行业常用单位,等于1/72英寸。 +合并拷贝,背景导出 + 参考链接: @@ -83,6 +85,19 @@ pt就是point,是印刷行业常用单位,等于1/72英寸。 - http://www.raiseai.com/ ## 常见任务 +垂直居中 + +``` +parentElement{ + position:relative; +} + +childElement{ + position: absolute; + top: 50%; + transform: translateY(-50%); +} +``` ### 左右等高 @@ -666,4 +681,259 @@ https://pine.fm/LearnToProgram/ http://www.qdaily.com/articles/42060.html https://zh.wikihow.com/%E5%AD%A6%E4%B9%A0%E7%BC%96%E7%A8%8B -nginx waf \ No newline at end of file +nginx waf + +前端系列教程 +https://chuanke.baidu.com/s5508922.html +http://growth.phodal.com/ + +关于PHP程序员解决问题的能力 +http://www.cnblogs.com/phpworld/p/8038581.html +ZH奶酪:编程语言入门经典100例【Python版】 +http://www.cnblogs.com/CheeseZH/archive/2012/11/05/2755107.html +https://www.coursera.org/learn/python + +JavaScript专题之惰性函数 https://segmentfault.com/a/1190000010783034 +关于尾递归的问题 https://segmentfault.com/q/1010000002705723 + +var pi = 3.14; +function area(r) { return 2 * pi * r;} +function iseven(n) {return n % 2 == 0;} + +function range(n, m) { console.log(n); (n < m) ? range(n + 1, m) : null;} +function rangef(n, m, f) { f(n); (n < m) ? rangef(n + 1, m, f) : null;} +function rangesum(n, m, sum) { return n < m ? n + rangesum(n + 1, m, sum) : n + sum; } +function rangesumf(n, m, sum, f) { return n < m ? f(n) + rangesumf(n + 1, m, sum, f) : f(n) + sum; } +function rangecond(n, m, cond) { cond(n) ? console.log(n) : null; (n < m) ? rangecond(n + 1, m, cond) : null;} + +7 of the Best Code Playgrounds +https://www.sitepoint.com/7-code-playgrounds/ + +Beginning Programming For Dummies +https://www.amazon.com/Beginning-Programming-Dummies-Wallace-Wang/dp/0470088702 +jQuery File Upload跨域上传 +https://www.cnblogs.com/ZHF/p/5057416.html +Javascript知识点:IIFE - 立即调用函数 +https://linghucong.js.org/2016/04/25/2016-04-08-Javascript-IIFE/ +技术面试需要掌握的基础知识 +https://github.com/CyC2018/Interview-Notebook + +### 函数组合 +function rangei(n) { + var i = 0; + return function() { + var ret = i < n ? i : null; + i = i + 1; + return ret; + }; +} + +function mapi(i, f) { + return function () { + var t = i(); + return t == null ? null : f(t); + }; +} + +function filteri(i, c) { + return function inner() { + var t = i(); + return t == null ? null : c(t) ? t : inner(); + }; +} + +function reducei(i, f, init_val) { + var t = i(); + return t == null ? init_val : reducei(i, f, f(init_val, t)); +} + +function iseven(x) { + return x % 2 == 0; +} + +function sqr(x) { + return x * x; +} + +function add(x, y) { + return x + y; +} + +//10 以内偶数的平方和 +reducei(mapi(filteri(rangei(10), iseven), sqr), add, 0) + +Jquery mobile change page +https://stackoverflow.com/questions/9738948/jquery-mobile-change-page +The Truth About Multiple H1 Tags in the HTML5 Era +https://webdesign.tutsplus.com/articles/the-truth-about-multiple-h1-tags-in-the-html5-era--webdesign-16824 + +How to set up Spark on Windows? +https://stackoverflow.com/questions/25481325/how-to-set-up-spark-on-windows + + +Git for windows 中文乱码解决方案 +https://segmentfault.com/a/1190000000578037 +Best way to find if an item is in a JavaScript array? [duplicate] +https://stackoverflow.com/questions/143847/best-way-to-find-if-an-item-is-in-a-javascript-array +后台管理UI的选择 +https://www.cnblogs.com/webenh/p/5815732.html + +配置 PHP 错误日志 + +mkdir /data/logs/php +chown apache:apache /data/logs/php + +php-fpm.conf: + + [global] + ; php-fpm pid文件 + pid = /usr/local/php/var/run/php-fpm.pid + ; php-fpm 错误日志路径 + error_log = /data/logs/php/error.log + ; php-fpm 记录错误日志等级 + log_level = notice + [www] + ; 记录错误到php-fpm的日志中 + ;catch_workers_output = yes + ; 慢日志 + slowlog = /data/logs/php/www-slow.log + ; 关闭打印日志 + php_flag[display_errors] = off + ; 错误日志 + php_admin_value[error_log] = /data/logs/php/www-error.log + ; 记录错误 + php_admin_flag[log_errors] = on + ; 内存使用量 + php_admin_value[memory_limit] = 32M + +php.ini: + + ; 错误日志 + log_errors = On + ; 显示错误 + display_errors = Off + ; 日志路径 + error_log = "/usr/local/lnmp/php/var/log/error_log" + ; 错误等级 + error_reporting = E_ALL&~E_NOTICE + + +nginx 路径匹配测试 + + location /test { + add_header Content-Type text/html; + return 200 'hello'; + } + +nginx php 测试 + + chmod o+x /root + chmod o+x /root/haohu + chmod o+x /root/haohu/phptest + + location ~ /test$ { + fastcgi_pass 127.0.0.1:9000; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /root/haohu/phptest/test.php; + } + +nginx 日志格式 + + log_format main '$time_iso8601 $status $request_time $upstream_response_time $remote_addr ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + +日志滚动 + /var/log/nginx/*.log + /data/log/nginx/*.log + /data/logs/techaction/*.php + /data/logs/php/*.log + /var/log/php-fpm/*log + { + daily + rotate 30 + missingok + notifempty + compress + sharedscripts + + postrotate + /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true + /bin/kill -SIGUSR1 `cat /var/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true + endscript + } + + +Linux日志文件总管——logrotate +https://linux.cn/article-4126-1.html +Linux中find常见用法示例 +http://www.cnblogs.com/wanqieddy/archive/2011/06/09/2076785.html + +找到 /data/logs 目录下所有 15 天之前修改过的 .php 和 .log 文件删除掉,删除前加确认,去掉确认的话,把 -ok 改成 -exec +find /data/logs/ -type f \( -name '*.php' -o -name '*.log' \) -mtime +15 -print -ok rm {} \; + + + +防止文件误删:http://www.cnblogs.com/lihaozy/archive/2012/08/17/2643784.html + + mkdir -p ~/.trash + alias rm=trash + alias r=trash + alias rl='ls ~/.trash' + alias ur=undelfile + + undelfile() + { + mv -i ~/.trash/$@ ./ + } + + trash() + { + mv $@ ~/.trash/ + } + + cleartrash() + { + read -p "clear sure?[n]" confirm + [ $confirm == 'y' ] || [ $confirm == 'Y' ] && /usr/bin/rm -rf ~/.trash/* + } + +[译] Node.js 8: util.promisify() +https://segmentfault.com/a/1190000009743481 +Node.js Async Best Practices & Avoiding the Callback Hell +https://blog.risingstack.com/node-js-async-best-practices-avoiding-callback-hell-node-js-at-scale/ +Nodejs 中使用 Async/Await +https://www.jianshu.com/p/0837dde8dcd5 + +promisify + async + + (async () => { + const fs = require('fs'); + const util = require('util'); + + const readFile = util.promisify(fs.readFile); + + const txt = await readFile('./notes.txt'); + console.log(txt); + })(); + +好吧,CSS3 3D transform变换,不过如此! +http://www.zhangxinxu.com/wordpress/2012/09/css3-3d-transform-perspective-animate-transition/ +客栈说书:CSS遮罩CSS3 mask/masks详细介绍 +http://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/ +新手应该如何学习 PHP 语言? +https://www.zhihu.com/question/20003635/answer/338733500 +目前最详细的PHP培训课程安排 +http://baijiahao.baidu.com/s?id=1563138980186749&wfr=spider&for=pc + +CSS3 box-pack 属性 +http://www.w3school.com.cn/cssref/pr_box-pack.asp +设计一个灵活的、可维护CSS和SVG饼图SVG +https://www.jianshu.com/p/f6526355de54 +SVG中stroke-dasharray及stroke-dashoffset属性 +https://blog.csdn.net/u014291497/article/details/78409350 +纯CSS实现帅气的SVG路径描边动画效果 +http://www.zhangxinxu.com/wordpress/2014/04/animateion-line-drawing-svg-path-%E5%8A%A8%E7%94%BB-%E8%B7%AF%E5%BE%84/ + +https://secbone.com/ +https://github.com/Secbone \ No newline at end of file diff --git a/php/wawa framework/Readme.md b/php/wawa framework/Readme.md new file mode 100644 index 00000000..14527a7f --- /dev/null +++ b/php/wawa framework/Readme.md @@ -0,0 +1,9 @@ +# Wawa Framework + +迷你 php 框架 + +- 自动路由: `get`, `post` +- 数据库操作封装: `fetch`, `execute` +- 配置文件支持: `config` +- view 层封装:`render` +- 其它工具函数:`redirect`, `e`, `send404` \ No newline at end of file diff --git a/php/wawa framework/config.php b/php/wawa framework/config.php new file mode 100644 index 00000000..29001c56 --- /dev/null +++ b/php/wawa framework/config.php @@ -0,0 +1,11 @@ + 'mysql', + 'dbhost' => 'localhost', + 'dbName' => 'test', + 'dbuser' => 'root', + 'dbpass' => 'password', + 'dbport' => 3307, +]; \ No newline at end of file diff --git a/php/wawa framework/controllers/index.php b/php/wawa framework/controllers/index.php new file mode 100644 index 00000000..924a19e8 --- /dev/null +++ b/php/wawa framework/controllers/index.php @@ -0,0 +1,40 @@ +get('index', function($w) { + $rows = $w->fetch('http://github.com/SELECT%20*%20from%20users'); + $w->render('views/index.php', ['rows' => $rows]); +}); + +$w->get('new', function($w) { + $w->render('views/user_form.php', ['action' => 'new']); +}); + +$w->get('modify', function($w) { + $rows = $w->fetch('http://github.com/SELECT%20*%20from%20users%20where%20id%20=%20?', [$_GET['id']]); + $w->render('views/user_form.php', ['action' => 'modify', 'user' => $rows[0]]); +}); + +$w->post('add', function($w) { + $sql = 'INSERT INTO `users`(`username`, `nickname`, `password`, `created_at`) VALUES (?, ?, ?, ?)'; + $w->execute($sql, [$_POST['username'], $_POST['nickname'], $_POST['password'], date('Y-m-d H:i:s')]); + $w->redirect('index'); +}); + +$w->post('update', function($w) { + $sql = 'update users set nickname=?, password=? where id = ?'; + $w->execute($sql, [$_POST['nickname'], $_POST['password'], $_POST['id']]); + $w->redirect('index'); +}); + +$w->get('remove', function($w) { + $rows = $w->fetch('http://github.com/SELECT%20*%20from%20users%20where%20id%20=%20?', [$_GET['id']]); + $w->render('views/user_delete.php', ['user' => $rows[0]]); +}); + +$w->post('remove', function($w) { + $w->execute('delete from users where id = ?', [$_POST['id']]); + $w->redirect('index'); +}); + +$w->run(); \ No newline at end of file diff --git a/php/wawa framework/public/index.php b/php/wawa framework/public/index.php new file mode 100644 index 00000000..7e96bcf8 --- /dev/null +++ b/php/wawa framework/public/index.php @@ -0,0 +1,4 @@ + + + + + +

config['site_name'] ?>

+

新建

+ + + + + + + + + + +
e($row['id']) ?>e($row['username']) ?>e($row['nickname']) ?>e($row['created_at']) ?> + 修改 + 删除 +
+ + \ No newline at end of file diff --git a/php/wawa framework/views/user_delete.php b/php/wawa framework/views/user_delete.php new file mode 100644 index 00000000..38105963 --- /dev/null +++ b/php/wawa framework/views/user_delete.php @@ -0,0 +1,18 @@ + + + + + +

config['site_name'] ?>

+

删除用户

+
+ + +

用户名:e($data['user']['username']) ?>

+ +

昵称:e($data['user']['nickname']) ?>

+ +

+
+ + \ No newline at end of file diff --git a/php/wawa framework/views/user_form.php b/php/wawa framework/views/user_form.php new file mode 100644 index 00000000..d264f6a4 --- /dev/null +++ b/php/wawa framework/views/user_form.php @@ -0,0 +1,26 @@ + + + + + +

config['site_name'] ?>

+

+
+ ' : ''?> + +

用户名: + value="e(isset($data['user'])) ? $data['user']['username'] : '' ?>" + >

+ +

昵称:

+ +

密码:

+

+
+ + \ No newline at end of file diff --git a/php/wawa framework/wawa.php b/php/wawa framework/wawa.php new file mode 100644 index 00000000..3dc22735 --- /dev/null +++ b/php/wawa framework/wawa.php @@ -0,0 +1,139 @@ +config = $config; + $this->sitePrefix = self::$sitePrefix; + $this->dsn = "{$config['db']['dbms']}:host={$config['db']['dbhost']};dbname={$config['db']['dbName']};port={$config['db']['dbport']};charset=utf8"; + } + + public function execute($sql, $args=[]) { + $conn = $this->_conn(); + $this->_execute($conn, $sql, $args); + $conn = null; + } + + public function fetch($sql, $args=[]) { + $conn = $this->_conn(); + $st = $this->_execute($conn, $sql, $args); + $ret = $st->fetchAll(); + $conn = null; + return $ret; + } + + public function render($view, $data=[]) { + $view = $this->_joinpath($view); + if (!file_exists($view)) die("$view not found."); + include($view); + } + + public function e($str) { + return htmlspecialchars($str); + } + + public function isPost() { + return $_SERVER['REQUEST_METHOD'] == 'POST'; + } + + public function redirect($url) { + if (strpos('http://', $url) !== 0 && strpos('https://', $url) !== 0) $url = $this->sitePrefix . $url; + header("Location: $url"); + } + + public function run() { + $this->_doAction(); + } + + public function get($action, $handler) { + self::$_getHandlers[$action] = $handler; + } + + public function post($action, $handler) { + self::$_postHandlers[$action] = $handler; + } + + public function send404($msg="File Not Found.\n") { + header("HTTP/1.0 404 Not Found"); + echo $msg; + die(); + } + + private function _execute($conn, $sql, $args) { + $st = $conn->prepare($sql); + $st->execute($args); + return $st; + } + + private function _conn() { + try { + return new PDO($this->dsn, $this->config['db']['dbuser'], $this->config['db']['dbpass']); + } catch (PDOException $e) { + die ("Error!: " . $e->getMessage() . "
"); + } + } + + private function _doAction() { + $actionName = self::$actionName; + $handlers = $this->isPost() ? self::$_postHandlers : self::$_getHandlers; + if (empty($handlers[$actionName])) return $this->send404("action $actionName not found."); + $handlers[$actionName]($this); + } + + /* begin static method */ + public static function init() { + $config = []; + $config_file = self::_joinpath('config.php'); + if (!file_exists($config_file)) die("$config_file file not found"); + require_once($config_file); + + self::$config = $config; + self::$sitePrefix = self::$config['site_prefix']; + } + + public static function runRoute() { + self::_parseRoute(); + self::_loadController(); + } + + private static function _parseRoute() { + // 取出 request_uri,去掉 query string 部分: '/me/xxx?id=1' => '/me/xxx' + $request_uri = $_SERVER['REQUEST_URI']; + $pos = strpos($request_uri, '?'); + if ($pos !== false) $request_uri = substr($request_uri, 0, $pos); + + + // 去掉 sitePrefix 前缀:'/me/xxx' => 'xxx' + $pos = strpos($request_uri, self::$sitePrefix); + if ($pos === 0) $request_uri = substr($request_uri, strlen(self::$sitePrefix)); + + // 取出 controllerName 和 actionName + $arr = explode('/', $request_uri); + self::$controllerName = empty($arr[0]) ? 'index' : $arr[0]; + self::$actionName = empty($arr[1]) ? 'index' : $arr[1]; + } + + private static function _loadController() { + $controllerFile = self::_joinpath('controllers/' . self::$controllerName . '.php'); + if (!file_exists($controllerFile)) return self::send404("controller {$this->controllerName} not found."); + include($controllerFile); + } + + private static function _joinpath($path) { + return join(DIRECTORY_SEPARATOR, [__DIR__, $path]); + } + /* end static method */ +} \ No newline at end of file From 7b26b840f1348a3bd48b0c233b10103acf0dc14f Mon Sep 17 00:00:00 2001 From: haohu Date: Mon, 7 May 2018 18:19:43 +0800 Subject: [PATCH 006/137] config --- php/wawa framework/Readme.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/php/wawa framework/Readme.md b/php/wawa framework/Readme.md index 14527a7f..a0553df6 100644 --- a/php/wawa framework/Readme.md +++ b/php/wawa framework/Readme.md @@ -6,4 +6,22 @@ - 数据库操作封装: `fetch`, `execute` - 配置文件支持: `config` - view 层封装:`render` -- 其它工具函数:`redirect`, `e`, `send404` \ No newline at end of file +- 其它工具函数:`redirect`, `e`, `send404` + +Apache 配置 + +``` + + Alias /me "D:/xampp/me/public" + + Options FollowSymLinks + AllowOverride All + Require all granted + + RewriteEngine on + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php/$1 [L] + + +``` \ No newline at end of file From ce4714da9aa9fe009c1d3f36918186fbb886c899 Mon Sep 17 00:00:00 2001 From: haohu Date: Mon, 13 Aug 2018 08:30:43 +0800 Subject: [PATCH 007/137] update --- docker/ssh-ubuntu/Dockerfile | 31 +++ mypost/tech_note.md | 396 ++++++++++++++++++++++++++++++++++- scratch/hello.sb2 | Bin 0 -> 292606 bytes 3 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 docker/ssh-ubuntu/Dockerfile create mode 100644 scratch/hello.sb2 diff --git a/docker/ssh-ubuntu/Dockerfile b/docker/ssh-ubuntu/Dockerfile new file mode 100644 index 00000000..e6f979b9 --- /dev/null +++ b/docker/ssh-ubuntu/Dockerfile @@ -0,0 +1,31 @@ +# 基础镜像信息 +FROM ubuntu:14.04 + +# 维护者信息 +MAINTAINER onlytiancai onlytiancai@gmail.com + +# 更新apt缓存、安装ssh服务 +RUN apt-get update && apt-get install -y openssh-server +RUN mkdir -p /var/run/sshd /root/.ssh +RUN sed -ir 's/^PermitRootLogin\s.*/PermitRootLogin yes/ig' /etc/ssh/sshd_config +RUN echo 'UseDNS no' >> /etc/ssh/sshd_config +RUN echo "root:123456" | chpasswd + + +# Set the locale +RUN locale-gen en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 +RUN echo 'LANG="en_US.UTF-8"' > /etc/default/locale +RUN echo 'LANGUAGE="en_US:en"' >> /etc/default/locale + +# 配置免密要和自启动脚本 +ADD run.sh /run.sh +RUN chmod 755 /run.sh + +# 暴露22端口 +EXPOSE 22 + +# 设置脚本自启动 +CMD ["/run.sh"] diff --git a/mypost/tech_note.md b/mypost/tech_note.md index c88668c7..26d5efd4 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -844,6 +844,7 @@ nginx 日志格式 '"$http_user_agent" "$http_x_forwarded_for"'; 日志滚动 + /var/log/nginx/*.log /data/log/nginx/*.log /data/logs/techaction/*.php @@ -936,4 +937,397 @@ https://blog.csdn.net/u014291497/article/details/78409350 http://www.zhangxinxu.com/wordpress/2014/04/animateion-line-drawing-svg-path-%E5%8A%A8%E7%94%BB-%E8%B7%AF%E5%BE%84/ https://secbone.com/ -https://github.com/Secbone \ No newline at end of file +https://github.com/Secbone + +# 快速学习一门语言 +- 打字练习: + - 数字,字母,下划线 + - 标点符号:+-*/\%(){};"',.!<>[]?^| +- 输出 + - 打印字符串 + - 打印数字 + - 混合打印 + - 输出复杂信息 + - 格式化输出 +- 字符串连接 +- 数学运算: + - * / % +- 关系运算: > < == != >= <= +- 逻辑运算: && || ! +- 变量和赋值: = +- 条件语句和关系操作符:两个数谁最大 +- 多重条件语句和逻辑操作符:三个数谁最大 +- 循环语句:打印 5 次 Hello +- for 循环:打印 10 以内的偶数 +- 数组基本操作 + - 打印数组 + - 获取数组长度 + - 获取指定索引元素 + - 修改指定索引元素的值 + - 数组末尾增加元素 + - 某元素是否存在 + - 遍历数组 + - 删除指定索引元素 + - 重排数组 + - 任意位置插入元素 + - 连接成字符串 + - 练习 + - 找出一个数组中最大的数 + - 找出一个数组中最大的奇数 + - 求出数组中所有奇数的和 +- 字符串基本操作 + - 字符串长度 + - 字符串连接 + - 转换大小写 + - 去掉首尾指定字符 + - 用某字符在首尾填充 + - 分割成数组 + - 比较两个字符串 + - 获取某子串的位置 + - 是否存在某个子串 + - 替换某个子串 + - 获取子串 + - 是否以某子串开头或结尾 + - 练习 + - 判断某字符是否为大写 + - 把字符串中的每个单词首字母大写 + - 按大写字母分割字符串 +- 字典基本操作 + - 根据键获取值 + - 添加键值对 + - 修改键值对 + - 删除指定键 + - 某键是否存在 + - 遍历字典 +- 时间操作 + - 设置时区 + - 获取当前时间的时间戳 + - 获取当前时间格式化字符串 +- 文件操作:打开并逐行读取文件 +- 类型判断 + - 是否为数字 + - 是否为整型 + - 是否为浮点数 + - 是否为字符串 + - 是否为 null + - 是否为 数组 + - 是否为空 +- 类型转换 + - 数字转字符串 + - 字符串转数字 +- 函数 + - 函数定义和使用 + - 匿名函数和闭包 + - 返回函数的函数 + - 参数为函数的函数 + - 函数的递归调用 +- 面向对象 + - 类 + - 方法 + - 字段 + - 静态字段 + - 子类 + +数组: +- get set remove insert length +- push pop shift unshit slice indexof +- startswitch endswitch contains +- map filter reduce +- removeCond groupby sortby countby + +字典 +- add set get remove +树 + +输入输出文件 + +Android Studio: + +- 下载带 Sdk 版本 +- 修改字体 +- 修改编码 +- 自动提示快捷键修改 +- 去掉拼写检查 +- 设置 git 目录 +- 禁用不需要的插件 +- 自动导入 +- File –> Other Settings –> Default Project Structure +- idea.properties disable.android.first.run=true +- ANDROID_SDK_HOME 环境变量指向 D:\android-home 把 .android 目录拷过去 +- 禁止自动打开上次的工程 +- 禁止代码折叠 + +Android: + +- 加载中效果 +- 短暂提示 +- 网络访问要用AsyncTask +- 小图标 +- 全局错误挂接 +- 优雅退出 +- 打日志 +- 每页的标题 +- 返回按钮 +- 导航条一直存在 + + +安卓异步任务类AsyncTask——突出一个简单、好用 +https://blog.csdn.net/jerrycqu/article/details/49357191 +Android OkHttp的基本用法 +https://www.jianshu.com/p/c478d7a20d03 +Toolbar的简单使用 +https://blog.csdn.net/monalisatearr/article/details/78415585 +ListView中自定义adapter的封装 +https://www.cnblogs.com/smyhvae/p/4477079.html +Gson解析——从简单数据到复杂数据 +https://www.jianshu.com/p/886f7b7fca7d +LoadingBar - 如何更优雅的使用Loading +https://blog.csdn.net/aa464971/article/details/70197394 +NavigationView的使用 +https://blog.csdn.net/bskfnvjtlyzmv867/article/details/70245826 +Android中处理崩溃异常 +https://blog.csdn.net/liuhe688/article/details/6584143 + + + + +Android Studio 入门级教程( +https://www.cnblogs.com/abao0/archive/2017/06/02/6934023.html + +详解 Android 的 Activity 组件 +https://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/ +Android之自定义Adapter的ListView +http://www.cnblogs.com/topcoderliu/archive/2011/05/07/2039862.html +想写个app,在哪里可以找到icon素材? +https://www.zhihu.com/question/40639915?sort=created +https://github.com/konifar/android-material-design-icon-generator-plugin +Android学习 - 美化ListView +https://blog.csdn.net/wolflz/article/details/45078107 +Android开发之漂亮Button样式 +https://www.jianshu.com/p/e5e8a98fc5d9 + + +图标制作 +https://www.iconfinder.com/editor/ +https://www.flaticon.com/free-icons/programming-language/2 +https://glyphter.com/ +https://www.shutterstock.com/zh/image-vector/workplace-programmer-coder-desktop-pc-laptop-705161689?src=Kd61QRsGtq1hI9vEL7LU2w-1-49 +https://iconsflow.com/editor + +APP第三方微信登录与公众号数据打通 +https://www.jianshu.com/p/18b1288f4c41 + +Flex 布局教程:语法篇 +http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html +Flex 布局教程:实例篇 +http://www.ruanyifeng.com/blog/2015/07/flex-examples.html + +Programmed Lessons in QBasic +http://chortle.ccsu.edu/QBasic/index.html +中等职业教育国家规划教材·编程语言基础:QBASIC语言(计算机及应用专业) +https://item.jd.com/10493424.html + +wget https://npm.taobao.org/mirrors/node/v8.9.3/node-v8.9.3-linux-x64.tar.xz + +tar -xzvf node-v8.9.3-linux-x64.tar.gz +tar -xvf node-v8.9.3-linux-x64.tar +ln -s /root/node-v8.9.3-linux-x64/bin/node /usr/local/bin/node +ln -s /root/node-v8.9.3-linux-x64/bin/npm /usr/local/bin/npm +npm -v +npm install -g cnpm --registry=https://registry.npm.taobao.org + +$ sudo npm install forever -g #安装 +$ forever start app.js #启动 +$ forever stop app.js #关闭 +$ forever start -l forever.log -o out.log -e err.log app.js #输出日志和错误 + + +## Apache backend for www.quancha.cn ## +upstream apachephp { + server ip:8080; #Apache +} + +## Start www.quancha.cn ## +server { + listen 80; + server_name www.quancha.cn; + + access_log logs/quancha.access.log main; + error_log logs/quancha.error.log; + root html; + index index.html index.htm index.php; + + ## send request back to apache ## + location / { + proxy_pass http://apachephp; + + #Proxy Settings + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_max_temp_file_size 0; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + } +} + +How to easily convert utf8 tables to utf8mb4 in MySQL 5.5 +https://dba.stackexchange.com/questions/8239/how-to-easily-convert-utf8-tables-to-utf8mb4-in-mysql-5-5 + +不翻墙也能找到免费优质素材,7个免费商用素材库墙裂推荐 +https://zhuanlan.zhihu.com/p/28508804 + +微服务分布式事务Saga模式简介 +http://www.jdon.com/49307 + +大数据告诉你:为啥近5年来Python如此火爆? +https://www.sohu.com/a/196290953_704222 +kvm管理平台webvirtmgr的部署 +https://www.jianshu.com/p/160272d81ac3 +Comfortable interface for KVM? (with PCIe Passthrough) +https://www.centos.org/forums/viewtopic.php?t=52640 + +CentOS之7与6的区别 +https://www.cnblogs.com/Csir/p/6746667.html +开源虚拟化管理平台Ovirt简介和配置环境搭建 +https://www.2cto.com/os/201202/120678.html +libvirt apps +https://libvirt.org/apps.html#web +十分钟带你理解Kubernetes核心概念 +http://dockone.io/article/932 +使用 Docker/LXC 迅速启动一个桌面系统 +https://www.oschina.net/question/54100_137626 +在Docker中运行桌面应用 +https://yq.aliyun.com/articles/224645# +docker-desktop +https://github.com/rogaha/docker-desktop +图形化界面的 docker ? +https://www.zhihu.com/question/34493859 +Windows Docker 安装 +http://www.runoob.com/docker/windows-docker-install.html +https://blog.csdn.net/tina_ttl/article/details/51372604 +后端架构师技术图谱 +https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84 + +termtosvg +A Linux terminal recorder written in Python that renders your command line sessions as standalone SVG animations. +https://github.com/nbedos/termtosvg + +如何编写技术解决方案 +https://wenku.baidu.com/view/b42e31a1760bf78a6529647d27284b73f2423635.html?from=search + +p5.js +http://ofcourse.io/biao-ti-3/ + +腾讯云 使用DockerHub加速器 +https://cloud.tencent.com/document/product/457/9113 +理解Docker(7):Docker 存储 - AUFS +https://www.cnblogs.com/sammyliu/p/5931383.html +[授权发表]基于 ssh + Xpra 构建 Docker 桌面系统 +https://blog.csdn.net/tinylab/article/details/45443563 +CentOS6下docker的安装和使用 +https://www.cnblogs.com/zhangzhen894095789/p/6641981.html?utm_source=itdadao&utm_medium=referral + +How to stop docker pull +https://stackoverflow.com/questions/29486032/how-to-stop-docker-pull +DOCKER_OPTS do not work in config file /etc/default/docker +https://stackoverflow.com/questions/27763340/docker-opts-do-not-work-in-config-file-etc-default-docker + +Set Docker_Opts in centos +https://stackoverflow.com/questions/26166550/set-docker-opts-in-centos + +vi /etc/sysconfig/docker + OPTIONS=--registry-mirror=https://mirror.ccs.tencentyun.com +sudo service docker restart + +netstat -tnpl +CONTAINER_ID=$(sudo docker run -d -p 2222:22 rogaha/docker-desktop) + +echo $(sudo docker logs $CONTAINER_ID | sed -n 1p) +User: docker Password: eengoch3ooK5 +docker port $CONTAINER_ID 22 + +/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT +/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT + +然后保存: + +/etc/rc.d/init.d/iptables save +centos 5.3,5.4以上的版本需要用 +service iptables save + +人人都该学编程? +https://www.jiemodui.com/Item/22041 + +How to Install PHP 7 in CentOS 7 +https://www.tecmint.com/install-php-7-in-centos-7/ + + +### .vimrc + +set nocp +set ts=4 +set sw=4 +set smarttab +set et +set ambiwidth=double +colo torte +set nu + +set encoding=UTF-8 +set langmenu=zh_CN.UTF-8 +language message zh_CN.UTF-8 +set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1 +set fileencoding=utf-8 + + +syntax on +filetype plugin indent on + +### .bashrc + +alias rm='rm -i' +alias cp='cp -i' +alias mv='mv -i' +alias vi='vim' + +# Source global definitions +if [ -f /etc/bashrc ]; then + . /etc/bashrc +fi + +export LC_ALL='zh_CN.utf8' + + + + + +Programmed Lessons in QBasic +http://chortle.ccsu.edu/QBasic/index.html 发现这个 qb 教程还挺好的 + +Shiro的三种授权(十二) +https://www.cnblogs.com/qlqwjy/p/7257616.html +走进Java(五)JSTL和EL表达式 +https://blog.csdn.net/u010096526/article/details/50038365 +JSTL获取Parameter参数 +https://blog.csdn.net/kevinxxw/article/details/50884649 +自定义页面方法:${fns:sayHelloToName('zxw')} +https://blog.csdn.net/zhengxiangwen/article/details/40652019 +JSTL表达式的使用(c:if)以及在JS中使用 +https://blog.csdn.net/weistin/article/details/80027218 + + +eclipse ERMaster插件安装 利用ERMaster快速生成db维护文档 +https://my.oschina.net/dajianguo/blog/1622944 + + +Eclipse启动时禁用不必要的验证。 +https://blog.csdn.net/u012726702/article/details/51758596 + +单词补全 Alt + / \ No newline at end of file diff --git a/scratch/hello.sb2 b/scratch/hello.sb2 new file mode 100644 index 0000000000000000000000000000000000000000..b67c815580ffd1b6eee9f6ccd6c3f1b44a681c7b GIT binary patch literal 292606 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%0D|)tgcEeldD@jY`61anw?jR4i>PQ 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 literal 0 HcmV?d00001 From deaf9a4ae83dedd6d3613369cf4354c06ed8dea8 Mon Sep 17 00:00:00 2001 From: haohu Date: Mon, 27 Aug 2018 08:34:03 +0800 Subject: [PATCH 008/137] x --- javascript/print_fibonacci.html | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 javascript/print_fibonacci.html diff --git a/javascript/print_fibonacci.html b/javascript/print_fibonacci.html new file mode 100644 index 00000000..a8e2e66f --- /dev/null +++ b/javascript/print_fibonacci.html @@ -0,0 +1,23 @@ + + + \ No newline at end of file From a3be8c9b669e240fcb8867ca3647ad19b32cc901 Mon Sep 17 00:00:00 2001 From: haohu Date: Mon, 27 Aug 2018 11:10:48 +0800 Subject: [PATCH 009/137] update --- javascript/print_fibonacci.html | 49 +++++++++++---- mypost/tech_note.md | 106 +++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 14 deletions(-) diff --git a/javascript/print_fibonacci.html b/javascript/print_fibonacci.html index a8e2e66f..654ac2da 100644 --- a/javascript/print_fibonacci.html +++ b/javascript/print_fibonacci.html @@ -1,23 +1,46 @@ \ No newline at end of file diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 26d5efd4..9c793cf6 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1330,4 +1330,108 @@ https://my.oschina.net/dajianguo/blog/1622944 Eclipse启动时禁用不必要的验证。 https://blog.csdn.net/u012726702/article/details/51758596 -单词补全 Alt + / \ No newline at end of file +单词补全 Alt + / + + +window---->preferences---->general----->content types----->Text------>java properties file---->UTF-8---->update ------>ok + +Mybatis整合Spring -- typeAliasesPackage +https://blog.csdn.net/sky786905664/article/details/51801933 + +重启 PHP-FPM + +sudo kill -usr2 $(cat /var/run/php-fpm/php-fpm.pid) +查看 PHP 配置 + +php --ini + +解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题 +https://blog.csdn.net/z69183787/article/details/19911935 + +Tomcat中server.xml配置详解 +https://www.cnblogs.com/yanghua1012/p/5869192.html +tomcat context元素属性介绍 +http://outofmemory.cn/code-snippet/3035/tomcat-context-element-property-introduction +tomcat 8.0特性 +https://blog.csdn.net/hmy1106/article/details/51270761 +Tomcat 7 的七大新特性 +http://www.iteye.com/news/17928/ +Tomcat 5.5.x到Tomcat 6.0(tomcat6新特性及变化) +https://blog.csdn.net/wlanye/article/details/8570891 + +SpringMVC重要注解(四)@ModelAttribute +https://blog.csdn.net/lovesomnus/article/details/78873089 +java怎么用一行代码初始化ArrayList +https://www.itstrike.cn/Question/e74b36fa-c01f-4254-87ec-e549df2abebe.html + +JS 物理引擎 +Physics engine in your JavaScript program +http://slicker.me/javascript/matter.htm +黑苹果 +https://github.com/kholia/OSX-KVM +命令行下的电子表格 +https://github.com/andmarti1424/sc-im +A* +http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html +可视化 +https://qiao.github.io/PathFinding.js/visual/ +游戏算法 +https://www.redblobgames.com/ +4种方法让SpringMVC接收多个对象 +https://blog.csdn.net/lutinghuan/article/details/46820023 +Objects binding in Spring MVC form +https://stackoverflow.com/questions/10138715/objects-binding-in-spring-mvc-form +Spring 4 官方文档学习 Web MVC 框架 +https://www.cnblogs.com/t3306/p/7244134.html +Allow binding=false on @ModelAttribute +https://github.com/spring-projects/spring-framework/commit/2e7470b27f0eaae042334cd86f212cd958676be0 +SpringMVC表单标签和表单标签简介 +https://blog.csdn.net/hp_yangpeng/article/details/51906654 +Myths about /dev/urandom +https://www.2uo.de/myths-about-urandom +With Undefined Behavior, Anything is Possible +https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html +How to Read 100s of Millions of Records per Second from a Single Disk +https://clemenswinter.com/2018/08/13/how-read-100s-of-millions-of-records-per-second-from-a-single-disk/ +Introduction to Go Modules +https://roberto.selbach.ca/intro-to-go-modules/ + +轻量级前端框架 +https://mithril.js.org/ + +Docker的web端管理平台对比(DockerUI 、Shipyard、Portainer、Daocloud) +https://blog.csdn.net/qq273681448/article/details/75007828 +Docker可视化管理工具Portainer的安装配置及使用 +https://blog.csdn.net/bbwangj/article/details/80973219 +Swarm -- 搭建Docker集群 +https://blog.csdn.net/a632189007/article/details/78756339 + +How to use UTF-8 in resource properties with ResourceBundle +https://stackoverflow.com/questions/4659929/how-to-use-utf-8-in-resource-properties-with-resourcebundle +Eclipse 在线安装properties编辑插件 +https://www.cnblogs.com/DylanZ/p/6428709.html + + +稍等两分钟,就会出现插件列表,选择PropertiesEditor,然后Next. + +Where does forever store console.log output? +https://stackoverflow.com/questions/21021186/where-does-forever-store-console-log-output + +forever start -o out.log -e err.log my-script.js + + +Learn You a Haskell for Great Good! +http://learnyouahaskell.com/chapters + +owncloud+collabora 实现网盘在线预览 +https://blog.csdn.net/a295184686/article/details/78632706 + +Epigrams on Programming +http://pu.inf.uni-tuebingen.de/users/klaeren/epigrams.html + + +程序员不能把自己全部的时间都用来换钱,而是要拿出20%~40%的可支配时间来学习、尝试和应用新的技术,让自己的技术栈保持连续更新。 + + +Guacamole 通过浏览器远程访问服务器 +https://www.jianshu.com/p/ebaba8ca17de \ No newline at end of file From 95345f282e299630aa72a6f536606a7e21f6a713 Mon Sep 17 00:00:00 2001 From: haohu Date: Thu, 30 Aug 2018 19:55:49 +0800 Subject: [PATCH 010/137] =?UTF-8?q?=E7=88=AC=E8=99=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/selenium/img1.png | Bin 0 -> 28973 bytes python/selenium/seleniumtest.py | 82 ++++++++++++++++++ ...54\344\270\234\347\210\254\350\231\253.md" | 25 ++++++ 3 files changed, 107 insertions(+) create mode 100644 python/selenium/img1.png create mode 100644 python/selenium/seleniumtest.py create mode 100644 "python/selenium/\344\272\254\344\270\234\347\210\254\350\231\253.md" diff --git a/python/selenium/img1.png b/python/selenium/img1.png new file mode 100644 index 0000000000000000000000000000000000000000..e45bd349b94260debe03cf2c4c315e30460b267a GIT binary patch literal 28973 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 P95QID>!}s1SO@$M%8H)j literal 0 HcmV?d00001 diff --git a/python/selenium/seleniumtest.py b/python/selenium/seleniumtest.py new file mode 100644 index 00000000..10c3be49 --- /dev/null +++ b/python/selenium/seleniumtest.py @@ -0,0 +1,82 @@ +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +import os +import time +import csv + +#引入chromedriver.exe +chromedriver = "C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe" +os.environ["webdriver.chrome.driver"] = chromedriver + +# 抓取计数及总量 +count = 0 +total = 10 + +urls = set() + +def get_goods(writer, browser): + global count, total + goods = browser.find_elements_by_class_name('gl-item') + + for good in goods: + + url = good.find_element_by_tag_name('a').get_attribute('href') + + # 跳过不符合要求的商品 + if url.find('item.jd.com') == -1: + continue + + # 跳过已经抓取过的商品 + if url in urls: + continue + + count = count + 1 + urls.add(url) + + name = good.find_element_by_css_selector('.p-name em').text + price = good.find_element_by_css_selector('.p-price i').text + commit = good.find_element_by_css_selector('.p-commit a').text + + if count > total: + print('抓取完成') + break + + writer.writerow([count, name, url, price, commit]) + + msg = ''' + 序号:%s + 商品:%s + 链接:%s + 价钱:%s + 评论: %s + ''' % (count, name, url, price, commit) + + print(msg, end='\n\n') + + # 翻页抓取下一页 + button = browser.find_element_by_partial_link_text('下一页') + button.click() + time.sleep(1) + get_goods(writer, browser) + + +def spider(url, keyword): + browser = webdriver.Chrome(chromedriver) + browser.get(url) + browser.implicitly_wait(3) + + try: + input = browser.find_element_by_id('key') + input.send_keys(keyword) + input.send_keys(Keys.ENTER) + + with open('output.csv', 'w', newline='') as csvfile: + writer = csv.writer(csvfile) + writer.writerow(['序号', '商品', '链接', '价钱', '评论']) + get_goods(writer, browser) + + finally: + browser.close() + +if __name__ == '__main__': + spider('https://www.jd.com/', keyword='python') diff --git "a/python/selenium/\344\272\254\344\270\234\347\210\254\350\231\253.md" "b/python/selenium/\344\272\254\344\270\234\347\210\254\350\231\253.md" new file mode 100644 index 00000000..653dd194 --- /dev/null +++ "b/python/selenium/\344\272\254\344\270\234\347\210\254\350\231\253.md" @@ -0,0 +1,25 @@ +# 安装 selenium + + pip3 install selenium + +--- + +# 安装 chromedriver + +- 因为官方网址被墙,需要到国内镜像下载 + - http://npm.taobao.org/mirrors/chromedriver/ +- 版本要对应 + - v2.40 对应 Chrome v66-68 +- 版本对应信息如下 + - http://npm.taobao.org/mirrors/chromedriver/2.40/notes.txt +- 把 `chromedriver.exe` 保存在 `Chrome` 安装目录 + - 一般是 `C:\Program Files (x86)\Google\Chrome\Application` 目录 + + +--- + +# 运行 DEMO + +双击 `seleniumtest.py` 即可 + +![](img1.png) \ No newline at end of file From a9a2cb0aa7eba363e26d544b792d5cdea3b14f15 Mon Sep 17 00:00:00 2001 From: haohu Date: Tue, 25 Sep 2018 08:45:27 +0800 Subject: [PATCH 011/137] go --- .gitignore | 13 +- docker/ssh-ubuntu/Dockerfile | 41 +- docker/ssh-ubuntu/aliyun.sources.list | 10 + docker/ssh-ubuntu/run.sh | 2 + javascript/print_fibonacci.html | 88 +- mypost/tech_note.md | 3195 ++++++++++++++----------- python/django/djangogirls/.gitignore | 5 + python/selenium/output.csv | 11 + python/untitled | 0 9 files changed, 1858 insertions(+), 1507 deletions(-) create mode 100644 docker/ssh-ubuntu/aliyun.sources.list create mode 100644 docker/ssh-ubuntu/run.sh create mode 100644 python/django/djangogirls/.gitignore create mode 100644 python/selenium/output.csv create mode 100644 python/untitled diff --git a/.gitignore b/.gitignore index 9504c754..040baf7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -*.o -*.pyc -*.log -*.out -*.swp -node/mydb/ +*.o +*.pyc +*.log +*.out +*.swp +node/mydb/ +*.ipynb \ No newline at end of file diff --git a/docker/ssh-ubuntu/Dockerfile b/docker/ssh-ubuntu/Dockerfile index e6f979b9..901310de 100644 --- a/docker/ssh-ubuntu/Dockerfile +++ b/docker/ssh-ubuntu/Dockerfile @@ -1,31 +1,34 @@ # 基础镜像信息 -FROM ubuntu:14.04 +FROM ubuntu:18.04 # 维护者信息 MAINTAINER onlytiancai onlytiancai@gmail.com +# 更新源 +RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak +ADD aliyun.sources.list /etc/apt/sources.list + # 更新apt缓存、安装ssh服务 -RUN apt-get update && apt-get install -y openssh-server +RUN apt-get update && apt-get install -y openssh-server vim RUN mkdir -p /var/run/sshd /root/.ssh -RUN sed -ir 's/^PermitRootLogin\s.*/PermitRootLogin yes/ig' /etc/ssh/sshd_config -RUN echo 'UseDNS no' >> /etc/ssh/sshd_config -RUN echo "root:123456" | chpasswd +# 配置 sshd 服务 +RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config +RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config +RUN echo 'UseDNS no' >> /etc/ssh/sshd_config +RUN echo "root:root" | chpasswd + +RUN apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + + # 自启动脚本 + ADD run.sh /run.sh + RUN chmod 755 /run.sh -# Set the locale -RUN locale-gen en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 -RUN echo 'LANG="en_US.UTF-8"' > /etc/default/locale -RUN echo 'LANGUAGE="en_US:en"' >> /etc/default/locale + # 暴露22端口 + EXPOSE 22 -# 配置免密要和自启动脚本 -ADD run.sh /run.sh -RUN chmod 755 /run.sh + # 设置脚本自启动 + CMD ["/run.sh"] -# 暴露22端口 -EXPOSE 22 -# 设置脚本自启动 -CMD ["/run.sh"] diff --git a/docker/ssh-ubuntu/aliyun.sources.list b/docker/ssh-ubuntu/aliyun.sources.list new file mode 100644 index 00000000..b2d8e354 --- /dev/null +++ b/docker/ssh-ubuntu/aliyun.sources.list @@ -0,0 +1,10 @@ +deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse diff --git a/docker/ssh-ubuntu/run.sh b/docker/ssh-ubuntu/run.sh new file mode 100644 index 00000000..ab19d0b5 --- /dev/null +++ b/docker/ssh-ubuntu/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/usr/sbin/sshd -D diff --git a/javascript/print_fibonacci.html b/javascript/print_fibonacci.html index 654ac2da..13e1f2f6 100644 --- a/javascript/print_fibonacci.html +++ b/javascript/print_fibonacci.html @@ -1,46 +1,44 @@ - - + + \ No newline at end of file diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 9c793cf6..8fff2a64 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1,1437 +1,1758 @@ -# 前台 - -## Web 发展历史 - -Web设计初衷是一个静态信息资源发布媒介,通过超文本标记语言(HTML)描述信息资源,通过统一资源标识符(URI)定位信息资源,通过超文本转移协议(HTTP)请求信息资源。 - -用通俗的一点的话来说,客户端(一般为浏览器)通过URL找到网站(如www.google.com),发出HTTP请求,服务器收到请求后返回HTML页面。 - -阶段 - -- Web 诞生 -- 动态内容出现:CGI -- Web 编程脚本语言:PHP / ASP / JSP -- 分布式企业计算平台:J2EE / .NET -- 框架横飞的年代:MVC,ORM -- 回归 Web 本质:REST -- 浏览器的魔术:Ajax -- 前端MVC:Angular / Backbone -- Javascript 在服务端的逆袭:NodeJs - -时间线 - -1. 1991年8月6日,Tim Berners Lee在alt.hypertext新闻组贴出了一份关于World Wide Web的简单摘要,标志了Web页面在Internet上的首次登场。 -2. Berners Lee在1993年建立了万维网联盟(World Wide Web Consortium,W3C),负责Web相关标准的制定。 -3. 1993年CGI(Common Gateway Interface)出现了,Web上的动态信息服务开始蓬勃兴起。 -4. 于是1994年的时候,PHP诞生了,PHP可以把程序(动态内容)嵌入到HTML(模版)中去执行,不仅能更好的组织Web应用的内容,而且执行效率比CGI还更高。 -5. 1995年NetScape公司设计的JavaScript被用作浏览器上运行脚本语言为网页增加动态性。 -6. 之后96年出现的ASP和98年出现的JSP本质上也都可以看成是一种支持某种脚本语言编程(分别是VB和Java)的模版引擎。 -7. 96年W3C发布了CSS1.0规范。CSS允许开发者用外联的样式表来取代难以维护的内嵌样式,而不需要逐个去修改HTML元素,这让HTML页面更加容易创建和维护。 -8. Web开始广泛用于构建大型应用时,在分布式、安全性、事务性等方面的要求催生了J2EE(现在已更名为Java EE)平台在1999年的诞生, -9. 2000年随之而来的.net平台,其ASP.net构件化的Web开发方式以及Visual Stidio.net开发环境的强大支持,大大降低了开发企业应用的复杂度。 -10. 2001年出现的Hibernate就是其中的佼佼者,已经成为Java持久层的规范JPA的主要参考和实现。 -11. 比如2003年出现的Java开发框架Spring -12. 2004年出现的Struts就是当时非常流行的Java Web开发的MVC框架。 -13. 2005年出现的AJAX这个概念使得JavaScript再次大放异彩。 -14. 2004年出现的Ruby开发框架Rails -15. 2005出现的Python开发框架Django - -参考链接 - -- [Web开发技术发展历史](https://www.tianmaying.com/tutorial/web-history) -- [Web开发技术的演变](http://blog.jobbole.com/45170/) - - -## 切图 - -- 取色:可以用 FSCapture 来取 -- 字号大小:PS里一般用 pt 单位,要转换成网页上的px -- 字体设置: font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; -- 测量间距 - - "编辑"-"首选项"-"单位与标尺", 把单位设置为像素 px - - "视图"-"标尺"(Ctrl+R) 把标尺显示出来 - - 按住 Shift 键盘,从标尺拖出参考线,会自动对齐物体的边缘 - - 工具栏中选择标尺工具,按住 Shift 键(会自动垂直或水平),先点击第一条辅助线,再拖动到第二条辅助线,然后看信息栏里的 W 和 H,表示宽和高的信息 - - 量完尺寸后可以"视图"-"清除参考线" -- 矢量 Logo 导出: - - 通过图层的隐藏显示,找到 Logo 对应的矢量智能对象, - - 右键点击图层,转换为智能对象 - - 按住 Ctrl 单击智能对象图层,再 Ctrl + C 复制选中的内容 - - Ctrl + N 新建文件,图像宽高会根据复制内容大小自动选定,背景内容选择 "透明" - - Ctrl + V 粘贴内容,"文件"-"存储为 Web 所用格式",格式选择 PNG-24, 存储即可,如选 PNG-8 可能会有毛刺 -- 图片导出: - - 打开移动工具,选项里把"自动选择"的勾打上, - - 点击要切割的图片,图层面板一般会自动选择图片所在的图层,即使定位不到具体图层,也能定位到图层组 - - 如果该图片是组合图形,或者有图层效果,或者有一个背景图一个遮罩,则按住 Ctrl 选中多个图层,右键合并图层 - - 按住 Ctrl 单击图层,Ctrl + C, Ctrl + N, Ctrl + V, Ctrl + Shif + Alt + S - - -字体大小的设置单位,常用的有2种:px、pt。这两个有什么区别呢? -先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点; -pt就是point,是印刷行业常用单位,等于1/72英寸。 - -合并拷贝,背景导出 - - -参考链接: - -- [PT与PX区别](https://www.douban.com/note/155032221/) -- [ps标尺和参考线知识点及快捷键](http://www.ittribalwo.com/article/1625.html) - - -## 经典页面 - -- [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) -- http://www.raiseai.com/ - -## 常见任务 -垂直居中 - -``` -parentElement{ - position:relative; -} - -childElement{ - position: absolute; - top: 50%; - transform: translateY(-50%); -} -``` - -### 左右等高 - -- [4 Methods For Creating Equal Height Columns In CSS](http://vanseodesign.com/css/equal-height-columns/) -- [Fluid Width Equal Height Columns](https://css-tricks.com/fluid-width-equal-height-columns/) -- [CSS布局——左定宽度右自适应宽度并且等高布局](https://www.w3cplus.com/css/two-cloumn-width-one-fixed-width-one-fluid-width) -- [Equal Height Column Layouts with Borders and Negative](https://www.smashingmagazine.com/2010/11/equal-height-columns-using-borders-and-negative-margins-with-css/) - - -### 背景图全屏 - -``` -html,body{ - width:100%; - height:100% -} - -body{ - width: 100%; - height:auto; - background:#343434 url("http://github.com/assets/img/bg.jpg") no-repeat; - background-size: cover; -} -``` - -## 参考链接 - -- [CSS Stacking Context里那些鲜为人知的坑](http://blog.angular.in/css-stacking-contextli-na-xie-xian-wei-ren-zhi-de-keng/) -- [HTML 和 Body 在 CSS 中的区别](https://csspod.com/html-vs-body-in-css/) -- [等宽列背后的表格布局算法](https://csspod.com/table-width-algorithms/) -- [Appendix D. Default style sheet for HTML 4](https://www.w3.org/TR/CSS2/sample.html) -- [学习CSS布局](https://www.w3cplus.com/css/learn-css-layout.html) -- [w3school HTML 系列教程](http://www.w3school.com.cn/h.asp) -- [CSS参考手册](http://www.css88.com/book/css/) -- [10 个最常见的 JavaScript 错误(以及如何避免它们)](http://www.css88.com/archives/9184) - -## todo - -- rem - - - -# 后台 - - -## 算法 - -[编程之法:面试和算法心得](https://github.com/julycoding/The-Art-Of-Programming-By-July) - - - -InfraredCounterParser InfraredCounterHandler InfraredCounterSender 等字符串,取出最后一个单词,如Parser, Handler, Sender 等 - -中英文混排,如何在英文和数字两边增加空格 - -# 数据库 -# 网络 - -# Java - -- [Google Guava官方教程(中文版)](http://ifeve.com/google-guava/) -- [Windows7下Maven环境搭建及其使用](http://blog.csdn.net/xuexiaoxu1990/article/details/52882664) - -## 环境变量配置 - - - -CLASSPATH= .;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar - -JAVA_HOME = C:/Program Files/Java/jdk1.5.0 - -PATH = %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin - - - - - -参考 - -- [classpath、path、JAVA_HOME的作用及JAVA环境变量配置](https://www.cnblogs.com/xwdreamer/archive/2010/09/08/2297098.html) - -# 软件工程 -## 代码风格 - -[编程之法:面试和算法心得 Code Style](https://github.com/julycoding/The-Art-Of-Programming-By-July) - -## UML - -### 类图 - -- 泛化: 泛化(generalization):表示is-a的关系,是对象之间耦合度最大的一种关系,子类继承父类的所有细节。直接使用语言中的继承表达。在类图中使用带三角箭头的实线表示,箭头从子类指向父类。 -- 实现(Realization):在类图中就是接口和实现的关系。在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。 -- 依赖(Dependency):对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。一个类调用被依赖类中的某些方法而得以完成这个类的一些职责。在类图使用带箭头的虚线表示,箭头从使用类指向被依赖的类。 -- 关联(Association) : 对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达。关联又分为一般关联、聚合关联与组合关联。在类图使用带箭头的实线表示,箭头从使用类指向被关联的类。可以是单向和双向。 -- 聚合(Aggregation) : 表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但如果公司倒闭,员工依然可以换公司。在类图使用空心的菱形表示,菱形从局部指向整体。 -- 组合(Composition) : 表示contains-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如公司和部门的关系,没有了公司,部门也不能存在了;调查问卷中问题和选项的关系;订单和订单选项的关系。在类图使用实心的菱形表示,菱形从局部指向整体。 - -参考 - -- [UML类图与类的关系详解](http://www.uml.org.cn/oobject/201104212.asp) - -## 需求工程 - - - -### 需求分类 - -软件需求包括3个不同的层次――业务需求、用户需求和功能需求。除此之外,每个系统还有各种非功能需求。 - -- **业务需求**(Business requirement)表示组织或客户高层次的目标。业务需求描述了组织为什么要开发一个系统,即组织希望达到的目标。 -- **用户需求**(user requirement)描述的是用户的目标,或用户要求系统必须能完成的任务,用户能使用系统来做些什么。用例、场景描述和事件都是表达用户需求的有效途径。 -- **功能需求**(functional requirement)规定开发人员必须在产品中实现的软件功能,用户利用这些功能来完成任务,满足业务需求。如“系统应该发送电子邮件来通知用户已接受其预定”。 -- **系统需求**(system requirement)用于描述包含多个子系统的产品(即系统)的顶级需求。系统可以只包含软件系统,也可以既包含软件又包含硬件子系统 -- **非功能需求** 为满足用户业务需求而必须具有且除功能需求以外的特性 - - 安全性 - - 可靠性 - - 易用性 - - 可维护性 - - 可移植性 - -### 需求开发 - - - -需求开发活动包括以下几个方面: - -1. 确定用户分类 -2. 获取每类用户的需求 -3. 了解实际用户任务和目标 -4. 分析源于用户的信息以获取用户任务需求、功能需求、业务规则、质量属性、建议解决方法和附加信息 -5. 将系统级的需求分为几个子系统 -6. 了解相关质量属性的重要性 -7. 商讨实施优先级的划分 -8. 编写成规格说明和模型。 -9. 评审需求规格说明 - - -### 用例描述 - -用例图描述了参与者要求系统能“做什么”,但是缺乏描述系统该“怎么做”的细节。一般情况下,每个用例应具有一个用例描述。 - -#### 用例描述说明 - - - -- **用例名称**:用例名称应该表明用户的意图或用例的用途,例如:借阅图书、归还图书、预定图书等。 -- **用例编号**: UC 0001 -- **简要说明**:对用例进行简要说明,描述该用例的作用,说明应当简明扼要。 -- **参与者**:与此用例相关的参与者列表 -- **前置条件**:执行用例之前系统必须满足的条件,例如:当学生借阅图书时,借阅图书用例需要获取学生的借阅证信息,如果学生使用了一个已经被注销的借阅证,那么借阅图书用例就不能执行。 -- **后置条件**:后置条件将在用例成功完成以后得到满足,它提供了系统的部分描述。例如:当学生借阅图书成功后,借阅图书用例应该提供该学生的所有借阅信息。 -- **基本操作流程**:指参与者在用例中所遵循的主逻辑路径。例如,借阅图书用例的基本操作流程如下: - - (1) 图书管理员输入借阅证信息 - - (2) 系统检查读者是否有超期的借阅信息 - - (3) 系统检查读者的借书数量是否已经达到借书限额 - - (4) 图书管理员输入要借阅的图书信息 - - (5) 系统将读者的借阅信息保存到数据库中 -- **可选操作流程**:指参与者在用例中所遵循的次逻辑路径,通常是指出现异常或发生错误的情况下所遵循的路径。 -- **涉及数据**:填写该用例涉及的相关信息,如图书名字,价格,ISBN号,出版日期等 - -#### 完整用例示例 - - - -| 主执行者 | 请求者 | -| --------- | ---------------------------------------- | -| 语境中的目标 | 请求者通过系统买东西,并得到说买的东西。不包括付款方面的内容。 | -| 范围 | 业务——整个购买机制,包括电子的和非电子的,正如人们在公司中说见到的一样。 | -| 层次 | 概要 | -| 项目相关人员和利益 | 请求者:希望得到她订购的东西,并且操作要简单。公司:希望控制花费,但允许必要的购买。供货商:希望得到任何已发货物的货款。 | -| 前置条件 | 无 | -| 最小保证 | 每一个发出的订单都已经获得有效认证者的许可。订单具有可跟踪性,以便公司只对收到的有效货物开账单。 | -| 成功保证 | 求者得到货物,修改预算,记入借方。 | -| 触发事件 | 请求者决定买东西。 | -| 主成功场景 | 1. 请求者:发起一个请求。2. 批准者:检查预算中的资金,检查货物的价格,完成提交请求。3. 买者:检查仓库的存货,找出最好的供货商。4. 认证者:验证批准者的签名。5. 买者:完成订购请求,向供货商发出PO(订单)。6. 供货商:把货物发送给接收者,得到发货收据(这一点超出了本系统的设计范围)。7. 接收者:记录发货情况;向请求者发送货物。8. 请求者:设置请求已被满足标志。 | -| 扩展 | 1a)请求者不知道供货商和货物价格:不填写这些内容,然后继续。1b)在收到货物之前的任意时刻,请求者都可以修改或取消请求:如果取消,则把这个请求从执行处理中取消。(从系统中删除吗?)如果降低价格,则不影响其处理过程。如果提高价格,则把请求送回批准者。2a)批准者不知道供货商或货物价格:不填写这些内容,留待买者填写或返回。2b)批准者不是请求者的经理:只是批准者签名仍然可行。2c)批准者拒绝申请:送回给请求者,要其修改或删除。3a)买者在仓库中找到货物:将存货先发出,并从申请者要求的总购买者中减去已经发出的这部分货物量,然后继续。3b)买者填写在前面活动中没有填写的供货商和价格信息:请求重新发回给批准者。4a)认证者拒绝批准者:发回请求者,并将此请求从执行处理中取消。5a)请求涉及到多个供货商:买者创建多个PO5b)买者将多个请求合并:相同的过程,但是用被合并的请求标记PO6a)供货商没有按时发货:系统发出没有发货警告。7a)部分发货:接收者在PO上做部分发货标记,然后继续。7b)多个请求PO的部分发货:接收者给每个请求分配货物数量,然后继续。8a)货物不对或质量不合格:请求者拒绝接收所发送的货物。8b)请求者已经离开公司:买者同请求者的经理进行核实,或者重新指派申请者,或者返还货物并取消请求。 | -| 技术和数据变动列表 | 无 | -| 优先级 | 多种 | -| 发行版本 | 几个 | -| 响应时间 | 多样 | -| 使用频率 | 3/天 | -| 主执行者的渠道 | 网络浏览器、邮件系统或类似系统 | -| 次要执行者 | 供货商 | -| 次要执行者的渠道 | 传真、电话或汽车 | -| 未解决的问题 | 什么时候从系统中删除被取消的请求?要取消一个请求需要那些权限?谁能修改一个请求的内容?请求中需要保留哪些修改历史记录?当请求者拒绝已经发送的货物时,会发生什么情况?申请和订货在运作上有什么不同? 订购如何参考和使用内部存货? | - -#### 用例执行步骤的10大准则 - -(1)使用简单的语法; -句子结构应该非常简单:主语……谓语动词……直接宾语……前置短语 -例如 系统……从帐户余额中扣除……一定数量…… - -(2)明确地写出“谁控制球”; -作者举了踢足球的场景的例子,说明了不管步骤的执行者如何变化,都要遵循(1)描述的格式。 - -(3)从俯视的角度来编写用例; -从用户的角度来写用例,而不是从系统内部来描述系统 - -~~(4)显示过程向前推移;~~ - -(5)显示执行者的意图而不是动作; -通过操纵系统的用户界面来描述用户的动作,这是在编写用例时常见的一种严重错误,它使得编写的目标处于一个很低的层次。我把它叫做“界面细节描述(interface detail description)”。在需求文档中,我们只关心界面所要达到的意图,总结在执行者之间传递的信息。可将这些低层次的步骤合并成一个步骤。 - -~~(6)包含“合理”的活动集;~~ - -(7)“确认”而不是“检查是否” -这是一个经常犯的错误,写用例不是写程序流程,不需要用选择语法,需要选择的时候,在扩展场景里体现 - -(8)可选择地提及时间限制; - -(9)习惯用语:“用户让系统A与系统B交互”; -要分开来写,用户与系统A怎么怎么样,然后系统A和系统B怎么怎么样,这样用户才能看的懂。 - -(10)习惯用语:“循环执行步骤X到Y,直到条件满足”; -同(7),但如果需要重复的话,可直接在重复的步骤的前面和后面说明即可。 - -总之,这10大原则,目的就是为了让用例成为用户和开发人员沟通的桥梁,所以语言要简单易懂,而且要逻辑清晰。 - -### 参考链接 - - - -- [需求入门: 需求工程=需求开发+需求管理](http://www.uml.org.cn/RequirementProject/201005285.asp) -- [软件需求规格说明书模板](https://jingyan.baidu.com/article/6dad5075eae10da123e36e80.html) -- [描述用例](http://blog.csdn.net/wlanye/article/details/7445676) -- [软件需求3个层次――业务需求、用户需求和功能需求](https://www.cnblogs.com/litian/articles/2047981.html) -- [非功能性六大点](https://jingyan.baidu.com/article/90bc8fc80960f1f653640ce0.html) -- [《编写有效用例》学习笔记](http://lib.csdn.net/article/softwaretest/24322) - - - - - -## 文档 - -### 文档列表 - -1. 可行性分析报告 -2. 项目开发计划 -3. **软件需求说明书** -4. **概要设计说明书** -5. 详细设计说明书 -6. **用户操作手册、运维部署文档** -7. 测试计划 -8. 测试分析报告 -9. 开发进度月报 -10. 项目开发总结报告 -11. 软件维护手册 -12. 软件问题报告 -13. 软件修改报告 - -### 需求分析文档 - -内容 - -- 项目背景 -- 使用角色 -- 功能需求 - - 功能划分 - - 用例图 - - 用例描述 -- 性能需求 - - 用户数评估 - - 响应时间要求 - - 可靠性需求 -- 用户界面 - -### 概要设计文档 - -对大部分的公司来说,概要设计文档是唯一的设计文档,对后面的开发、测试、实施、维护工作起到关键性的影响。 - -内容 - -- 功能介绍 - - 用例图 -- 整体架构 - - 层次结构 - - 模块划分 - - 模块间调用关系 - - 包图、组件图 -- 接口设计 - - 对外接口草案 - - 模块间接口草案 -- 模块设计 - - 职责描述 - - 类图 - - 算法描述(如有) -- 数据流设计 - - 流程图 - - 序列图 - - 状态图 -- 数据库概要设计 - - 表设计 - - 核心语句 -- 主要界面 -- 部署结构 - - 部署图 -- 非功能需求 - - 性能需求 - - 安全需求 - - 扩展性需求 -- 编码规范 - - 代码风格 - - 数据库命名规范 - - 接口规范 - -### 详细设计文档 - -同概要设计文档,只是需要更详细,比如 - -- 类图要精确到字段,方法的类型, -- 数据库设计要精确到字段类型,索引设计 -- 核心算法要画出序列图及伪代码 - -### 运维部署文档 - -内容 - -- 硬件需求 -- 软件需求 -- 外部依赖 -- 部署步骤 -- 配置说明 -- 维护命令:启动、停止、重启 -- 监控指标 -- 如何升级 -- 常见问题处理 -- 数据备份 - -### 参考链接 - -- [软件开发文档范例](http://zz563143188.iteye.com/blog/1835305) -- [概要设计文档编写规范](http://blog.csdn.net/nengyu/article/details/3758312) - -# 工具 - -## 其它 - -- [f.lux - 全天候保护眼睛健康软件!自动调整屏幕色温减少蓝光防疲劳,长时间玩电脑必备!](https://www.iplaysoft.com/flux.html) -- [MarkDown 写 ppt](https://yhatt.github.io/marp/) -- [在线根据 markdown 生成 ppt](http://www.vmfor.com/ppt/index.html) - -## 编辑器 - - - -### 功能需求 - - - -- 行号显示 -- 语法高亮 -- 自动识别编码 -- 自动缩进 -- 智能提示 -- 列编辑 -- 代码片段 -- 代码折叠 -- 新建文件模板 -- 多标签 -- 会话管理 -- 分屏 -- 书签 -- 多次光标点跳转 - -### VS Code - -快捷键 - -- 注释切换:ctrl +/ -- 格式化代码: Alt + Shift + F -- 自动换行:Alt + Z -- 选区移动:Alt + ↑/↓ -- 多选修改:Ctrl + D - -### Eclipse - -- 格式化代码:Ctrl + Shift + F - -参考链接: - -- [为什么我选择使用 VS Code进行前端开发?](https://zhuanlan.zhihu.com/p/28631442) -- [TextMate代码片段语法](https://manual.macromates.com/en/snippets) - -# 物联网 - -## 参考链接 - -[关于RS232 和 RS485 的区别](http://blog.csdn.net/foreverhuylee/article/details/23375079) - -## 硬件技术 - - -### 串口 - -串行接口(Serial port)又称“串口”,主要用于串行式逐位数据传输。 - -串行接口按电气标准及协议来分,包括RS-232-C、RS-422、RS485、USB等。 - -- RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。 -- USB是近几年发展起来的新型接口标准,主要应用于高速数据传输领域。 - -**RS-232-C :也称标准串口**,是目前最常用的一种串行通讯接口。电脑一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。 - -**RS-485** :为扩展应用范围,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围。 - -**Universal Serial Bus(通用串行总线) :简称USB,** USB接口是电脑主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,新的USB 2.0可达480Mbps; - -**RJ-45接口** :是以太网最为常用的接口, 8个位置(8针)的模块化插孔或者插头 - - - -串口属性: - -- PortName 串口名 -- BaudRate 获取或设置串行波特率bit/s 默认值9600 -- DataBits 获取或设置每个字节的标准数据位长度 默认值8 -- StopBits 获取或设置每个字节的标准停止位数 默认值One -- Parity 获取或设置奇偶校验检查协议 默认值None - - - -参考 - -- [C#中的串口通信](http://www.cnblogs.com/51net/p/6050840.html) - -### IC卡,ID卡,M1卡,射频卡的区别 - -IC卡全称集成电路卡(Integrated Circuit Card),又称智能卡(Smart Card)。可读写,容量大,有加密功能,数据记录可靠,使用更方便,如一卡通系统、消费系统等 - -ID卡全称身份识别卡(Identification Card),是一种不可写入的感应卡,含固定的编号, - -[门禁卡是选择IC卡好还是ID卡好](https://jingyan.baidu.com/article/54b6b9c0d8056f2d593b474c.html) - - - - -### RFID - -从概念上来讲,RFID类似于[条码扫描](https://baike.baidu.com/item/%E6%9D%A1%E7%A0%81%E6%89%AB%E6%8F%8F),对于条码技术而言,它是将已编码的条形码附着于目标物并使用专用的扫描读写器利用光信号将信息由条形磁传送到扫描读写器;而RFID则使用专用的RFID读写器及专门的可附着于目标物的RFID标签,利用频率信号将信息由RFID标签传送至RFID读写器。 - -[射频识别系统](https://baike.baidu.com/item/%E5%B0%84%E9%A2%91%E8%AF%86%E5%88%AB%E7%B3%BB%E7%BB%9F)最重要的优点是非接触识别,它能穿透雪、雾、冰、[涂料](https://baike.baidu.com/item/%E6%B6%82%E6%96%99)、[尘垢](https://baike.baidu.com/item/%E5%B0%98%E5%9E%A2)和条形码无法使用的恶劣环境阅读标签,并且阅读速度极快,大多数情况下不到100毫秒。 - -一维条形码的容量是50Bytes,二维条形码最大的容量可储存2至3000字符,RFID最大的容量则有数MegaBytes. - -现今的条形码印刷上去之后就无法更改,RFID标签则可以重复地新增、修改、删除RFID卷标内储存的数据,方便信息的更新。 - -### NFC - -NFC近场通信技术是由非接触式[射频识别](https://baike.baidu.com/item/%E5%B0%84%E9%A2%91%E8%AF%86%E5%88%AB)([RFID](https://baike.baidu.com/item/RFID))及互联互通技术整合演变而来,在单一芯片上结合感应式[读卡器](https://baike.baidu.com/item/%E8%AF%BB%E5%8D%A1%E5%99%A8)、感应式卡片和点对点的功能,能在短距离内与兼容设备进行识别和[数据交换](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E4%BA%A4%E6%8D%A2)。 - -NFC手机内置NFC芯片,比原先仅作为标签使用的RFID更增加了数据双向传送的功能,这个进步使得其更加适合用于电子货币支付的; - -NFC传输范围比RFID小,RFID的传输范围可以达到几米、甚至几十米,但由于NFC采取了独特的信号衰减技术,相对于RFID来说NFC具有距离近、带宽高、能耗低等特点。 - -应用方向不同。NFC看更多的是针对于消费类电子设备相互通讯,有源RFID则更擅长在长距离识别。 - -NFC的短距离通信特性正是其优点,由于耗电量低、一次只和一台机器链接,拥有较高的保密性与安全性,NFC有利于信用卡交易时避免被盗用。 - -### PLC - -### 继电器 - -继电器是具有隔离功能的自动开关元件,广泛应用于遥控、遥测、通讯、自动控制、机电一体化及电力电子设备中,是最重要的控制元件之一。 - -继电器一般都有能反映一定输入变量(如电流、电压、功率、阻抗、频率、温度、压力、速度、光等)的感应机构(输入部分);有能对被控电路实现“通”、“断”控制的执行机构(输出部分);在继电器的输入部分和输出部分之间,还有对输入量进行耦合隔离,功能处理和对输出部分进行驱动的中间机构(驱动部分)。 - -### 二进制协议 - -二进制常用操作 - -``` - - int value = 170; - void print(int x) => Console.WriteLine(Convert.ToString(x, 2).PadLeft(8, '0')); - int make_mask(int x) => 1 << x; - int set(int x, int i) => x | make_mask(i); - int unset(int x, int i) => x & ~make_mask(i); - bool isset(int x, int i) => (x & make_mask(i)) != 0; - - //170的二进制显示: 10101010 - print(value); - - // 右数第 5 位置 1 : 10111010 - print(set(value, 4)); - - // 右数第 4 位置 0: 10100010 - print(unset(value, 3)); - - // 右数第 4 位是否为1: true - Console.WriteLine(isset(value, 3)); - - //右数第 3 位是否为1: false - Console.WriteLine(isset(value, 2)); -``` - -### 其它 - -TTL电平信号之所以被广泛使用,原因是:通常我们采用二进制来表示数据。而且规定,+5V等价于逻辑“1”,0V等价于逻辑“0”。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。 - -GND是电线接地端的简写。代表地线或0线。这个地并不是真正意义上的地,是出于应用而假设的一个地,对于电源来说,它就是一个电源的负极。 - -VCC:电源电压(双极器件);电源电压(74系列数字电路); - -RXD 为接收数据的引脚,TXD 为发送数据的引脚。 - -DTE提供或接收数据,连接到调制解调器上的计算机就是一种DTE。DTE提供或接收数据,连接到网络中的用户端机器,主要是计算机和终端设备。 - -在网络端的连接设备称为DCE(Data-Communication Equipment)。DTE与进行信令处理的DCE相连。 -DTE通过DCE设备,例如,调制解调器,连接到数据网络。 - -RS232标准中的RTS与CTS:即请求发送/清除发送,用于半双工时的收发切换,属于辅助流控信号。半双工的意思是说,发的时候不收,收的时候不发。那么怎么区分收发呢?缺省时是DCE向DTE发送数据,当DTE决定向DCE发数据时,先有效RTS,表示DTE希望向DCE发送。一般DCE不能马上转换收发状态,DTE就通过监测CTS是否有效来判断可否发送,这样避免了DTE在DCE未准备好时发送所导致的数据丢失。 - -差分输入的是将两个输入端的差值作为信号,这样可以免去一些误差,比如你输入一个1V的信号电源有偏差,比实际输入要大0.1.就可以用差分输入1V和2V一减就把两端共有的那0.1误差剪掉了。单端输入无法去除这类误差。 - -在某些系统里,系统'地'被用作电压基准点。当'地'当作电压测量基准时,这种信号规划被称之为单端的。 - -差分信号的第一个好处是,因为你在控制'基准'电压,所以能够很容易地识别小信号。 -差分信号的第二个主要好处是,它对外部电磁干扰(EMI)是高度免疫的。 -差分信号提供的第三个好处是,在一个单电源系统,能够从容精确地处理'双极'信号。 - -上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用。下拉同理,也是将不确定的信号通过一个电阻钳位在低电平。 - -# 企业信息化 - -大多数OA产品功能集中在信息共享、行政办公领域,一些主流OA系统虽然引入了工作流,但相对比较封闭,开放性和扩展性不够。 - -BPM是一个开放性平台,不仅能实现所有OA的功能,还能满足企业内部系统之间集成的需求,在BPM引擎驱动下,企业的流程终会形成一个闭环。 - -ERP - -WMS - -MES - -ESB - -SOA - -- [智能MES解决方案](http://www.rtdsoft.com/channels/57.html) -- [制造执行系统(MES)选型与实施指南简版](https://wenku.baidu.com/view/052b5ef4a32d7375a41780c8.html) -- [OpenMES架构说明书](https://wenku.baidu.com/view/2a98711ec281e53a5802ffc8.html) - -## 快速开发框架 - -- 登录注册: Apache Shiro -- 组织机构 -- 权限管理: Apache Shiro -- 增删改查 -- 后台界面 -- 菜单管理 -- 工作流:Activity -- 报表:JasperReports - -参考 - -- http://www.jeecg.org/ -- [Java通用权限系统管理(Spring+springMVC+ibatis+Angularjs)](http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/liaodehong/article/details/53100313) -- [组织机构对象模型设计及实现](http://blog.csdn.net/wangpeng047/article/details/7280800) -- [LigerUI 快速开发UI框架](http://www.ligerui.com/) -- https://github.com/thinkgem/jeesite - -jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块 -http://blog.csdn.net/qing_gee/article/details/76223064 - - -# 未整理 - -[Avoid calling Invoke when the control is disposed](https://stackoverflow.com/questions/1874728/avoid-calling-invoke-when-the-control-is-disposed) - - - -http://blog.csdn.net/pkueecser/article/details/50610796 时间序列数据库的秘密 - - - -https://github.com/justjavac/ReplaceGoogleCDN Replace Google CDN - -https://stackoverflow.com/questions/31572580/how-covert-c-sharp-datetime-to-java-datetimeusing-joda-time -https://pine.fm/LearnToProgram/ -http://www.qdaily.com/articles/42060.html -https://zh.wikihow.com/%E5%AD%A6%E4%B9%A0%E7%BC%96%E7%A8%8B - -nginx waf - -前端系列教程 -https://chuanke.baidu.com/s5508922.html -http://growth.phodal.com/ - -关于PHP程序员解决问题的能力 -http://www.cnblogs.com/phpworld/p/8038581.html -ZH奶酪:编程语言入门经典100例【Python版】 -http://www.cnblogs.com/CheeseZH/archive/2012/11/05/2755107.html -https://www.coursera.org/learn/python - -JavaScript专题之惰性函数 https://segmentfault.com/a/1190000010783034 -关于尾递归的问题 https://segmentfault.com/q/1010000002705723 - -var pi = 3.14; -function area(r) { return 2 * pi * r;} -function iseven(n) {return n % 2 == 0;} - -function range(n, m) { console.log(n); (n < m) ? range(n + 1, m) : null;} -function rangef(n, m, f) { f(n); (n < m) ? rangef(n + 1, m, f) : null;} -function rangesum(n, m, sum) { return n < m ? n + rangesum(n + 1, m, sum) : n + sum; } -function rangesumf(n, m, sum, f) { return n < m ? f(n) + rangesumf(n + 1, m, sum, f) : f(n) + sum; } -function rangecond(n, m, cond) { cond(n) ? console.log(n) : null; (n < m) ? rangecond(n + 1, m, cond) : null;} - -7 of the Best Code Playgrounds -https://www.sitepoint.com/7-code-playgrounds/ - -Beginning Programming For Dummies -https://www.amazon.com/Beginning-Programming-Dummies-Wallace-Wang/dp/0470088702 -jQuery File Upload跨域上传 -https://www.cnblogs.com/ZHF/p/5057416.html -Javascript知识点:IIFE - 立即调用函数 -https://linghucong.js.org/2016/04/25/2016-04-08-Javascript-IIFE/ -技术面试需要掌握的基础知识 -https://github.com/CyC2018/Interview-Notebook - -### 函数组合 -function rangei(n) { - var i = 0; - return function() { - var ret = i < n ? i : null; - i = i + 1; - return ret; - }; -} - -function mapi(i, f) { - return function () { - var t = i(); - return t == null ? null : f(t); - }; -} - -function filteri(i, c) { - return function inner() { - var t = i(); - return t == null ? null : c(t) ? t : inner(); - }; -} - -function reducei(i, f, init_val) { - var t = i(); - return t == null ? init_val : reducei(i, f, f(init_val, t)); -} - -function iseven(x) { - return x % 2 == 0; -} - -function sqr(x) { - return x * x; -} - -function add(x, y) { - return x + y; -} - -//10 以内偶数的平方和 -reducei(mapi(filteri(rangei(10), iseven), sqr), add, 0) - -Jquery mobile change page -https://stackoverflow.com/questions/9738948/jquery-mobile-change-page -The Truth About Multiple H1 Tags in the HTML5 Era -https://webdesign.tutsplus.com/articles/the-truth-about-multiple-h1-tags-in-the-html5-era--webdesign-16824 - -How to set up Spark on Windows? -https://stackoverflow.com/questions/25481325/how-to-set-up-spark-on-windows - - -Git for windows 中文乱码解决方案 -https://segmentfault.com/a/1190000000578037 -Best way to find if an item is in a JavaScript array? [duplicate] -https://stackoverflow.com/questions/143847/best-way-to-find-if-an-item-is-in-a-javascript-array -后台管理UI的选择 -https://www.cnblogs.com/webenh/p/5815732.html - -配置 PHP 错误日志 - -mkdir /data/logs/php -chown apache:apache /data/logs/php - -php-fpm.conf: - - [global] - ; php-fpm pid文件 - pid = /usr/local/php/var/run/php-fpm.pid - ; php-fpm 错误日志路径 - error_log = /data/logs/php/error.log - ; php-fpm 记录错误日志等级 - log_level = notice - [www] - ; 记录错误到php-fpm的日志中 - ;catch_workers_output = yes - ; 慢日志 - slowlog = /data/logs/php/www-slow.log - ; 关闭打印日志 - php_flag[display_errors] = off - ; 错误日志 - php_admin_value[error_log] = /data/logs/php/www-error.log - ; 记录错误 - php_admin_flag[log_errors] = on - ; 内存使用量 - php_admin_value[memory_limit] = 32M - -php.ini: - - ; 错误日志 - log_errors = On - ; 显示错误 - display_errors = Off - ; 日志路径 - error_log = "/usr/local/lnmp/php/var/log/error_log" - ; 错误等级 - error_reporting = E_ALL&~E_NOTICE - - -nginx 路径匹配测试 - - location /test { - add_header Content-Type text/html; - return 200 'hello'; - } - -nginx php 测试 - - chmod o+x /root - chmod o+x /root/haohu - chmod o+x /root/haohu/phptest - - location ~ /test$ { - fastcgi_pass 127.0.0.1:9000; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /root/haohu/phptest/test.php; - } - -nginx 日志格式 - - log_format main '$time_iso8601 $status $request_time $upstream_response_time $remote_addr ' - '"$request" $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - -日志滚动 - - /var/log/nginx/*.log - /data/log/nginx/*.log - /data/logs/techaction/*.php - /data/logs/php/*.log - /var/log/php-fpm/*log - { - daily - rotate 30 - missingok - notifempty - compress - sharedscripts - - postrotate - /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true - /bin/kill -SIGUSR1 `cat /var/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true - endscript - } - - -Linux日志文件总管——logrotate -https://linux.cn/article-4126-1.html -Linux中find常见用法示例 -http://www.cnblogs.com/wanqieddy/archive/2011/06/09/2076785.html - -找到 /data/logs 目录下所有 15 天之前修改过的 .php 和 .log 文件删除掉,删除前加确认,去掉确认的话,把 -ok 改成 -exec -find /data/logs/ -type f \( -name '*.php' -o -name '*.log' \) -mtime +15 -print -ok rm {} \; - - - -防止文件误删:http://www.cnblogs.com/lihaozy/archive/2012/08/17/2643784.html - - mkdir -p ~/.trash - alias rm=trash - alias r=trash - alias rl='ls ~/.trash' - alias ur=undelfile - - undelfile() - { - mv -i ~/.trash/$@ ./ - } - - trash() - { - mv $@ ~/.trash/ - } - - cleartrash() - { - read -p "clear sure?[n]" confirm - [ $confirm == 'y' ] || [ $confirm == 'Y' ] && /usr/bin/rm -rf ~/.trash/* - } - -[译] Node.js 8: util.promisify() -https://segmentfault.com/a/1190000009743481 -Node.js Async Best Practices & Avoiding the Callback Hell -https://blog.risingstack.com/node-js-async-best-practices-avoiding-callback-hell-node-js-at-scale/ -Nodejs 中使用 Async/Await -https://www.jianshu.com/p/0837dde8dcd5 - -promisify + async - - (async () => { - const fs = require('fs'); - const util = require('util'); - - const readFile = util.promisify(fs.readFile); - - const txt = await readFile('./notes.txt'); - console.log(txt); - })(); - -好吧,CSS3 3D transform变换,不过如此! -http://www.zhangxinxu.com/wordpress/2012/09/css3-3d-transform-perspective-animate-transition/ -客栈说书:CSS遮罩CSS3 mask/masks详细介绍 -http://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/ -新手应该如何学习 PHP 语言? -https://www.zhihu.com/question/20003635/answer/338733500 -目前最详细的PHP培训课程安排 -http://baijiahao.baidu.com/s?id=1563138980186749&wfr=spider&for=pc - -CSS3 box-pack 属性 -http://www.w3school.com.cn/cssref/pr_box-pack.asp -设计一个灵活的、可维护CSS和SVG饼图SVG -https://www.jianshu.com/p/f6526355de54 -SVG中stroke-dasharray及stroke-dashoffset属性 -https://blog.csdn.net/u014291497/article/details/78409350 -纯CSS实现帅气的SVG路径描边动画效果 -http://www.zhangxinxu.com/wordpress/2014/04/animateion-line-drawing-svg-path-%E5%8A%A8%E7%94%BB-%E8%B7%AF%E5%BE%84/ - -https://secbone.com/ -https://github.com/Secbone - -# 快速学习一门语言 -- 打字练习: - - 数字,字母,下划线 - - 标点符号:+-*/\%(){};"',.!<>[]?^| -- 输出 - - 打印字符串 - - 打印数字 - - 混合打印 - - 输出复杂信息 - - 格式化输出 -- 字符串连接 -- 数学运算: + - * / % -- 关系运算: > < == != >= <= -- 逻辑运算: && || ! -- 变量和赋值: = -- 条件语句和关系操作符:两个数谁最大 -- 多重条件语句和逻辑操作符:三个数谁最大 -- 循环语句:打印 5 次 Hello -- for 循环:打印 10 以内的偶数 -- 数组基本操作 - - 打印数组 - - 获取数组长度 - - 获取指定索引元素 - - 修改指定索引元素的值 - - 数组末尾增加元素 - - 某元素是否存在 - - 遍历数组 - - 删除指定索引元素 - - 重排数组 - - 任意位置插入元素 - - 连接成字符串 - - 练习 - - 找出一个数组中最大的数 - - 找出一个数组中最大的奇数 - - 求出数组中所有奇数的和 -- 字符串基本操作 - - 字符串长度 - - 字符串连接 - - 转换大小写 - - 去掉首尾指定字符 - - 用某字符在首尾填充 - - 分割成数组 - - 比较两个字符串 - - 获取某子串的位置 - - 是否存在某个子串 - - 替换某个子串 - - 获取子串 - - 是否以某子串开头或结尾 - - 练习 - - 判断某字符是否为大写 - - 把字符串中的每个单词首字母大写 - - 按大写字母分割字符串 -- 字典基本操作 - - 根据键获取值 - - 添加键值对 - - 修改键值对 - - 删除指定键 - - 某键是否存在 - - 遍历字典 -- 时间操作 - - 设置时区 - - 获取当前时间的时间戳 - - 获取当前时间格式化字符串 -- 文件操作:打开并逐行读取文件 -- 类型判断 - - 是否为数字 - - 是否为整型 - - 是否为浮点数 - - 是否为字符串 - - 是否为 null - - 是否为 数组 - - 是否为空 -- 类型转换 - - 数字转字符串 - - 字符串转数字 -- 函数 - - 函数定义和使用 - - 匿名函数和闭包 - - 返回函数的函数 - - 参数为函数的函数 - - 函数的递归调用 -- 面向对象 - - 类 - - 方法 - - 字段 - - 静态字段 - - 子类 - -数组: -- get set remove insert length -- push pop shift unshit slice indexof -- startswitch endswitch contains -- map filter reduce -- removeCond groupby sortby countby - -字典 -- add set get remove -树 - -输入输出文件 - -Android Studio: - -- 下载带 Sdk 版本 -- 修改字体 -- 修改编码 -- 自动提示快捷键修改 -- 去掉拼写检查 -- 设置 git 目录 -- 禁用不需要的插件 -- 自动导入 -- File –> Other Settings –> Default Project Structure -- idea.properties disable.android.first.run=true -- ANDROID_SDK_HOME 环境变量指向 D:\android-home 把 .android 目录拷过去 -- 禁止自动打开上次的工程 -- 禁止代码折叠 - -Android: - -- 加载中效果 -- 短暂提示 -- 网络访问要用AsyncTask -- 小图标 -- 全局错误挂接 -- 优雅退出 -- 打日志 -- 每页的标题 -- 返回按钮 -- 导航条一直存在 - - -安卓异步任务类AsyncTask——突出一个简单、好用 -https://blog.csdn.net/jerrycqu/article/details/49357191 -Android OkHttp的基本用法 -https://www.jianshu.com/p/c478d7a20d03 -Toolbar的简单使用 -https://blog.csdn.net/monalisatearr/article/details/78415585 -ListView中自定义adapter的封装 -https://www.cnblogs.com/smyhvae/p/4477079.html -Gson解析——从简单数据到复杂数据 -https://www.jianshu.com/p/886f7b7fca7d -LoadingBar - 如何更优雅的使用Loading -https://blog.csdn.net/aa464971/article/details/70197394 -NavigationView的使用 -https://blog.csdn.net/bskfnvjtlyzmv867/article/details/70245826 -Android中处理崩溃异常 -https://blog.csdn.net/liuhe688/article/details/6584143 - - - - -Android Studio 入门级教程( -https://www.cnblogs.com/abao0/archive/2017/06/02/6934023.html - -详解 Android 的 Activity 组件 -https://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/ -Android之自定义Adapter的ListView -http://www.cnblogs.com/topcoderliu/archive/2011/05/07/2039862.html -想写个app,在哪里可以找到icon素材? -https://www.zhihu.com/question/40639915?sort=created -https://github.com/konifar/android-material-design-icon-generator-plugin -Android学习 - 美化ListView -https://blog.csdn.net/wolflz/article/details/45078107 -Android开发之漂亮Button样式 -https://www.jianshu.com/p/e5e8a98fc5d9 - - -图标制作 -https://www.iconfinder.com/editor/ -https://www.flaticon.com/free-icons/programming-language/2 -https://glyphter.com/ -https://www.shutterstock.com/zh/image-vector/workplace-programmer-coder-desktop-pc-laptop-705161689?src=Kd61QRsGtq1hI9vEL7LU2w-1-49 -https://iconsflow.com/editor - -APP第三方微信登录与公众号数据打通 -https://www.jianshu.com/p/18b1288f4c41 - -Flex 布局教程:语法篇 -http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html -Flex 布局教程:实例篇 -http://www.ruanyifeng.com/blog/2015/07/flex-examples.html - -Programmed Lessons in QBasic -http://chortle.ccsu.edu/QBasic/index.html -中等职业教育国家规划教材·编程语言基础:QBASIC语言(计算机及应用专业) -https://item.jd.com/10493424.html - -wget https://npm.taobao.org/mirrors/node/v8.9.3/node-v8.9.3-linux-x64.tar.xz - -tar -xzvf node-v8.9.3-linux-x64.tar.gz -tar -xvf node-v8.9.3-linux-x64.tar -ln -s /root/node-v8.9.3-linux-x64/bin/node /usr/local/bin/node -ln -s /root/node-v8.9.3-linux-x64/bin/npm /usr/local/bin/npm -npm -v -npm install -g cnpm --registry=https://registry.npm.taobao.org - -$ sudo npm install forever -g #安装 -$ forever start app.js #启动 -$ forever stop app.js #关闭 -$ forever start -l forever.log -o out.log -e err.log app.js #输出日志和错误 - - -## Apache backend for www.quancha.cn ## -upstream apachephp { - server ip:8080; #Apache -} - -## Start www.quancha.cn ## -server { - listen 80; - server_name www.quancha.cn; - - access_log logs/quancha.access.log main; - error_log logs/quancha.error.log; - root html; - index index.html index.htm index.php; - - ## send request back to apache ## - location / { - proxy_pass http://apachephp; - - #Proxy Settings - proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; - proxy_max_temp_file_size 0; - proxy_connect_timeout 90; - proxy_send_timeout 90; - proxy_read_timeout 90; - proxy_buffer_size 4k; - proxy_buffers 4 32k; - proxy_busy_buffers_size 64k; - proxy_temp_file_write_size 64k; - } -} - -How to easily convert utf8 tables to utf8mb4 in MySQL 5.5 -https://dba.stackexchange.com/questions/8239/how-to-easily-convert-utf8-tables-to-utf8mb4-in-mysql-5-5 - -不翻墙也能找到免费优质素材,7个免费商用素材库墙裂推荐 -https://zhuanlan.zhihu.com/p/28508804 - -微服务分布式事务Saga模式简介 -http://www.jdon.com/49307 - -大数据告诉你:为啥近5年来Python如此火爆? -https://www.sohu.com/a/196290953_704222 -kvm管理平台webvirtmgr的部署 -https://www.jianshu.com/p/160272d81ac3 -Comfortable interface for KVM? (with PCIe Passthrough) -https://www.centos.org/forums/viewtopic.php?t=52640 - -CentOS之7与6的区别 -https://www.cnblogs.com/Csir/p/6746667.html -开源虚拟化管理平台Ovirt简介和配置环境搭建 -https://www.2cto.com/os/201202/120678.html -libvirt apps -https://libvirt.org/apps.html#web -十分钟带你理解Kubernetes核心概念 -http://dockone.io/article/932 -使用 Docker/LXC 迅速启动一个桌面系统 -https://www.oschina.net/question/54100_137626 -在Docker中运行桌面应用 -https://yq.aliyun.com/articles/224645# -docker-desktop -https://github.com/rogaha/docker-desktop -图形化界面的 docker ? -https://www.zhihu.com/question/34493859 -Windows Docker 安装 -http://www.runoob.com/docker/windows-docker-install.html -https://blog.csdn.net/tina_ttl/article/details/51372604 -后端架构师技术图谱 -https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84 - -termtosvg -A Linux terminal recorder written in Python that renders your command line sessions as standalone SVG animations. -https://github.com/nbedos/termtosvg - -如何编写技术解决方案 -https://wenku.baidu.com/view/b42e31a1760bf78a6529647d27284b73f2423635.html?from=search - -p5.js -http://ofcourse.io/biao-ti-3/ - -腾讯云 使用DockerHub加速器 -https://cloud.tencent.com/document/product/457/9113 -理解Docker(7):Docker 存储 - AUFS -https://www.cnblogs.com/sammyliu/p/5931383.html -[授权发表]基于 ssh + Xpra 构建 Docker 桌面系统 -https://blog.csdn.net/tinylab/article/details/45443563 -CentOS6下docker的安装和使用 -https://www.cnblogs.com/zhangzhen894095789/p/6641981.html?utm_source=itdadao&utm_medium=referral - -How to stop docker pull -https://stackoverflow.com/questions/29486032/how-to-stop-docker-pull -DOCKER_OPTS do not work in config file /etc/default/docker -https://stackoverflow.com/questions/27763340/docker-opts-do-not-work-in-config-file-etc-default-docker - -Set Docker_Opts in centos -https://stackoverflow.com/questions/26166550/set-docker-opts-in-centos - -vi /etc/sysconfig/docker - OPTIONS=--registry-mirror=https://mirror.ccs.tencentyun.com -sudo service docker restart - -netstat -tnpl -CONTAINER_ID=$(sudo docker run -d -p 2222:22 rogaha/docker-desktop) - -echo $(sudo docker logs $CONTAINER_ID | sed -n 1p) -User: docker Password: eengoch3ooK5 -docker port $CONTAINER_ID 22 - -/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT -/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT - -然后保存: - -/etc/rc.d/init.d/iptables save -centos 5.3,5.4以上的版本需要用 -service iptables save - -人人都该学编程? -https://www.jiemodui.com/Item/22041 - -How to Install PHP 7 in CentOS 7 -https://www.tecmint.com/install-php-7-in-centos-7/ - - -### .vimrc - -set nocp -set ts=4 -set sw=4 -set smarttab -set et -set ambiwidth=double -colo torte -set nu - -set encoding=UTF-8 -set langmenu=zh_CN.UTF-8 -language message zh_CN.UTF-8 -set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1 -set fileencoding=utf-8 - - -syntax on -filetype plugin indent on - -### .bashrc - -alias rm='rm -i' -alias cp='cp -i' -alias mv='mv -i' -alias vi='vim' - -# Source global definitions -if [ -f /etc/bashrc ]; then - . /etc/bashrc -fi - -export LC_ALL='zh_CN.utf8' - - - - - -Programmed Lessons in QBasic -http://chortle.ccsu.edu/QBasic/index.html 发现这个 qb 教程还挺好的 - -Shiro的三种授权(十二) -https://www.cnblogs.com/qlqwjy/p/7257616.html -走进Java(五)JSTL和EL表达式 -https://blog.csdn.net/u010096526/article/details/50038365 -JSTL获取Parameter参数 -https://blog.csdn.net/kevinxxw/article/details/50884649 -自定义页面方法:${fns:sayHelloToName('zxw')} -https://blog.csdn.net/zhengxiangwen/article/details/40652019 -JSTL表达式的使用(c:if)以及在JS中使用 -https://blog.csdn.net/weistin/article/details/80027218 - - -eclipse ERMaster插件安装 利用ERMaster快速生成db维护文档 -https://my.oschina.net/dajianguo/blog/1622944 - - -Eclipse启动时禁用不必要的验证。 -https://blog.csdn.net/u012726702/article/details/51758596 - -单词补全 Alt + / - - -window---->preferences---->general----->content types----->Text------>java properties file---->UTF-8---->update ------>ok - -Mybatis整合Spring -- typeAliasesPackage -https://blog.csdn.net/sky786905664/article/details/51801933 - -重启 PHP-FPM - -sudo kill -usr2 $(cat /var/run/php-fpm/php-fpm.pid) -查看 PHP 配置 - -php --ini - -解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题 -https://blog.csdn.net/z69183787/article/details/19911935 - -Tomcat中server.xml配置详解 -https://www.cnblogs.com/yanghua1012/p/5869192.html -tomcat context元素属性介绍 -http://outofmemory.cn/code-snippet/3035/tomcat-context-element-property-introduction -tomcat 8.0特性 -https://blog.csdn.net/hmy1106/article/details/51270761 -Tomcat 7 的七大新特性 -http://www.iteye.com/news/17928/ -Tomcat 5.5.x到Tomcat 6.0(tomcat6新特性及变化) -https://blog.csdn.net/wlanye/article/details/8570891 - -SpringMVC重要注解(四)@ModelAttribute -https://blog.csdn.net/lovesomnus/article/details/78873089 -java怎么用一行代码初始化ArrayList -https://www.itstrike.cn/Question/e74b36fa-c01f-4254-87ec-e549df2abebe.html - -JS 物理引擎 -Physics engine in your JavaScript program -http://slicker.me/javascript/matter.htm -黑苹果 -https://github.com/kholia/OSX-KVM -命令行下的电子表格 -https://github.com/andmarti1424/sc-im -A* -http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html -可视化 -https://qiao.github.io/PathFinding.js/visual/ -游戏算法 -https://www.redblobgames.com/ -4种方法让SpringMVC接收多个对象 -https://blog.csdn.net/lutinghuan/article/details/46820023 -Objects binding in Spring MVC form -https://stackoverflow.com/questions/10138715/objects-binding-in-spring-mvc-form -Spring 4 官方文档学习 Web MVC 框架 -https://www.cnblogs.com/t3306/p/7244134.html -Allow binding=false on @ModelAttribute -https://github.com/spring-projects/spring-framework/commit/2e7470b27f0eaae042334cd86f212cd958676be0 -SpringMVC表单标签和表单标签简介 -https://blog.csdn.net/hp_yangpeng/article/details/51906654 -Myths about /dev/urandom -https://www.2uo.de/myths-about-urandom -With Undefined Behavior, Anything is Possible -https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html -How to Read 100s of Millions of Records per Second from a Single Disk -https://clemenswinter.com/2018/08/13/how-read-100s-of-millions-of-records-per-second-from-a-single-disk/ -Introduction to Go Modules -https://roberto.selbach.ca/intro-to-go-modules/ - -轻量级前端框架 -https://mithril.js.org/ - -Docker的web端管理平台对比(DockerUI 、Shipyard、Portainer、Daocloud) -https://blog.csdn.net/qq273681448/article/details/75007828 -Docker可视化管理工具Portainer的安装配置及使用 -https://blog.csdn.net/bbwangj/article/details/80973219 -Swarm -- 搭建Docker集群 -https://blog.csdn.net/a632189007/article/details/78756339 - -How to use UTF-8 in resource properties with ResourceBundle -https://stackoverflow.com/questions/4659929/how-to-use-utf-8-in-resource-properties-with-resourcebundle -Eclipse 在线安装properties编辑插件 -https://www.cnblogs.com/DylanZ/p/6428709.html - - -稍等两分钟,就会出现插件列表,选择PropertiesEditor,然后Next. - -Where does forever store console.log output? -https://stackoverflow.com/questions/21021186/where-does-forever-store-console-log-output - -forever start -o out.log -e err.log my-script.js - - -Learn You a Haskell for Great Good! -http://learnyouahaskell.com/chapters - -owncloud+collabora 实现网盘在线预览 -https://blog.csdn.net/a295184686/article/details/78632706 - -Epigrams on Programming -http://pu.inf.uni-tuebingen.de/users/klaeren/epigrams.html - - -程序员不能把自己全部的时间都用来换钱,而是要拿出20%~40%的可支配时间来学习、尝试和应用新的技术,让自己的技术栈保持连续更新。 - - -Guacamole 通过浏览器远程访问服务器 -https://www.jianshu.com/p/ebaba8ca17de \ No newline at end of file +# 前台 + +## Web 发展历史 + +Web设计初衷是一个静态信息资源发布媒介,通过超文本标记语言(HTML)描述信息资源,通过统一资源标识符(URI)定位信息资源,通过超文本转移协议(HTTP)请求信息资源。 + +用通俗的一点的话来说,客户端(一般为浏览器)通过URL找到网站(如www.google.com),发出HTTP请求,服务器收到请求后返回HTML页面。 + +阶段 + +- Web 诞生 +- 动态内容出现:CGI +- Web 编程脚本语言:PHP / ASP / JSP +- 分布式企业计算平台:J2EE / .NET +- 框架横飞的年代:MVC,ORM +- 回归 Web 本质:REST +- 浏览器的魔术:Ajax +- 前端MVC:Angular / Backbone +- Javascript 在服务端的逆袭:NodeJs + +时间线 + +1. 1991年8月6日,Tim Berners Lee在alt.hypertext新闻组贴出了一份关于World Wide Web的简单摘要,标志了Web页面在Internet上的首次登场。 +2. Berners Lee在1993年建立了万维网联盟(World Wide Web Consortium,W3C),负责Web相关标准的制定。 +3. 1993年CGI(Common Gateway Interface)出现了,Web上的动态信息服务开始蓬勃兴起。 +4. 于是1994年的时候,PHP诞生了,PHP可以把程序(动态内容)嵌入到HTML(模版)中去执行,不仅能更好的组织Web应用的内容,而且执行效率比CGI还更高。 +5. 1995年NetScape公司设计的JavaScript被用作浏览器上运行脚本语言为网页增加动态性。 +6. 之后96年出现的ASP和98年出现的JSP本质上也都可以看成是一种支持某种脚本语言编程(分别是VB和Java)的模版引擎。 +7. 96年W3C发布了CSS1.0规范。CSS允许开发者用外联的样式表来取代难以维护的内嵌样式,而不需要逐个去修改HTML元素,这让HTML页面更加容易创建和维护。 +8. Web开始广泛用于构建大型应用时,在分布式、安全性、事务性等方面的要求催生了J2EE(现在已更名为Java EE)平台在1999年的诞生, +9. 2000年随之而来的.net平台,其ASP.net构件化的Web开发方式以及Visual Stidio.net开发环境的强大支持,大大降低了开发企业应用的复杂度。 +10. 2001年出现的Hibernate就是其中的佼佼者,已经成为Java持久层的规范JPA的主要参考和实现。 +11. 比如2003年出现的Java开发框架Spring +12. 2004年出现的Struts就是当时非常流行的Java Web开发的MVC框架。 +13. 2005年出现的AJAX这个概念使得JavaScript再次大放异彩。 +14. 2004年出现的Ruby开发框架Rails +15. 2005出现的Python开发框架Django + +参考链接 + +- [Web开发技术发展历史](https://www.tianmaying.com/tutorial/web-history) +- [Web开发技术的演变](http://blog.jobbole.com/45170/) + + +## 切图 + +- 取色:可以用 FSCapture 来取 +- 字号大小:PS里一般用 pt 单位,要转换成网页上的px +- 字体设置: font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; +- 测量间距 + - "编辑"-"首选项"-"单位与标尺", 把单位设置为像素 px + - "视图"-"标尺"(Ctrl+R) 把标尺显示出来 + - 按住 Shift 键盘,从标尺拖出参考线,会自动对齐物体的边缘 + - 工具栏中选择标尺工具,按住 Shift 键(会自动垂直或水平),先点击第一条辅助线,再拖动到第二条辅助线,然后看信息栏里的 W 和 H,表示宽和高的信息 + - 量完尺寸后可以"视图"-"清除参考线" +- 矢量 Logo 导出: + - 通过图层的隐藏显示,找到 Logo 对应的矢量智能对象, + - 右键点击图层,转换为智能对象 + - 按住 Ctrl 单击智能对象图层,再 Ctrl + C 复制选中的内容 + - Ctrl + N 新建文件,图像宽高会根据复制内容大小自动选定,背景内容选择 "透明" + - Ctrl + V 粘贴内容,"文件"-"存储为 Web 所用格式",格式选择 PNG-24, 存储即可,如选 PNG-8 可能会有毛刺 +- 图片导出: + - 打开移动工具,选项里把"自动选择"的勾打上, + - 点击要切割的图片,图层面板一般会自动选择图片所在的图层,即使定位不到具体图层,也能定位到图层组 + - 如果该图片是组合图形,或者有图层效果,或者有一个背景图一个遮罩,则按住 Ctrl 选中多个图层,右键合并图层 + - 按住 Ctrl 单击图层,Ctrl + C, Ctrl + N, Ctrl + V, Ctrl + Shif + Alt + S + + +字体大小的设置单位,常用的有2种:px、pt。这两个有什么区别呢? +先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点; +pt就是point,是印刷行业常用单位,等于1/72英寸。 + +合并拷贝,背景导出 + + +参考链接: + +- [PT与PX区别](https://www.douban.com/note/155032221/) +- [ps标尺和参考线知识点及快捷键](http://www.ittribalwo.com/article/1625.html) + + +## 经典页面 + +- [Alloy Timer](http://alloyteam.github.io/AlloyTimer/) +- http://www.raiseai.com/ + +## 常见任务 +垂直居中 + +``` +parentElement{ + position:relative; +} + +childElement{ + position: absolute; + top: 50%; + transform: translateY(-50%); +} +``` + +### 左右等高 + +- [4 Methods For Creating Equal Height Columns In CSS](http://vanseodesign.com/css/equal-height-columns/) +- [Fluid Width Equal Height Columns](https://css-tricks.com/fluid-width-equal-height-columns/) +- [CSS布局——左定宽度右自适应宽度并且等高布局](https://www.w3cplus.com/css/two-cloumn-width-one-fixed-width-one-fluid-width) +- [Equal Height Column Layouts with Borders and Negative](https://www.smashingmagazine.com/2010/11/equal-height-columns-using-borders-and-negative-margins-with-css/) + + +### 背景图全屏 + +``` +html,body{ + width:100%; + height:100% +} + +body{ + width: 100%; + height:auto; + background:#343434 url("http://github.com/assets/img/bg.jpg") no-repeat; + background-size: cover; +} +``` + +## 参考链接 + +- [CSS Stacking Context里那些鲜为人知的坑](http://blog.angular.in/css-stacking-contextli-na-xie-xian-wei-ren-zhi-de-keng/) +- [HTML 和 Body 在 CSS 中的区别](https://csspod.com/html-vs-body-in-css/) +- [等宽列背后的表格布局算法](https://csspod.com/table-width-algorithms/) +- [Appendix D. Default style sheet for HTML 4](https://www.w3.org/TR/CSS2/sample.html) +- [学习CSS布局](https://www.w3cplus.com/css/learn-css-layout.html) +- [w3school HTML 系列教程](http://www.w3school.com.cn/h.asp) +- [CSS参考手册](http://www.css88.com/book/css/) +- [10 个最常见的 JavaScript 错误(以及如何避免它们)](http://www.css88.com/archives/9184) + +## todo + +- rem + + + +# 后台 + + +## 算法 + +[编程之法:面试和算法心得](https://github.com/julycoding/The-Art-Of-Programming-By-July) + + + +InfraredCounterParser InfraredCounterHandler InfraredCounterSender 等字符串,取出最后一个单词,如Parser, Handler, Sender 等 + +中英文混排,如何在英文和数字两边增加空格 + +# 数据库 +# 网络 + +# Java + +- [Google Guava官方教程(中文版)](http://ifeve.com/google-guava/) +- [Windows7下Maven环境搭建及其使用](http://blog.csdn.net/xuexiaoxu1990/article/details/52882664) + +## 环境变量配置 + + + +CLASSPATH= .;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar + +JAVA_HOME = C:/Program Files/Java/jdk1.5.0 + +PATH = %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin + + + + + +参考 + +- [classpath、path、JAVA_HOME的作用及JAVA环境变量配置](https://www.cnblogs.com/xwdreamer/archive/2010/09/08/2297098.html) + +# 软件工程 +## 代码风格 + +[编程之法:面试和算法心得 Code Style](https://github.com/julycoding/The-Art-Of-Programming-By-July) + +## UML + +### 类图 + +- 泛化: 泛化(generalization):表示is-a的关系,是对象之间耦合度最大的一种关系,子类继承父类的所有细节。直接使用语言中的继承表达。在类图中使用带三角箭头的实线表示,箭头从子类指向父类。 +- 实现(Realization):在类图中就是接口和实现的关系。在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。 +- 依赖(Dependency):对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。一个类调用被依赖类中的某些方法而得以完成这个类的一些职责。在类图使用带箭头的虚线表示,箭头从使用类指向被依赖的类。 +- 关联(Association) : 对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达。关联又分为一般关联、聚合关联与组合关联。在类图使用带箭头的实线表示,箭头从使用类指向被关联的类。可以是单向和双向。 +- 聚合(Aggregation) : 表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但如果公司倒闭,员工依然可以换公司。在类图使用空心的菱形表示,菱形从局部指向整体。 +- 组合(Composition) : 表示contains-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如公司和部门的关系,没有了公司,部门也不能存在了;调查问卷中问题和选项的关系;订单和订单选项的关系。在类图使用实心的菱形表示,菱形从局部指向整体。 + +参考 + +- [UML类图与类的关系详解](http://www.uml.org.cn/oobject/201104212.asp) + +## 需求工程 + + + +### 需求分类 + +软件需求包括3个不同的层次――业务需求、用户需求和功能需求。除此之外,每个系统还有各种非功能需求。 + +- **业务需求**(Business requirement)表示组织或客户高层次的目标。业务需求描述了组织为什么要开发一个系统,即组织希望达到的目标。 +- **用户需求**(user requirement)描述的是用户的目标,或用户要求系统必须能完成的任务,用户能使用系统来做些什么。用例、场景描述和事件都是表达用户需求的有效途径。 +- **功能需求**(functional requirement)规定开发人员必须在产品中实现的软件功能,用户利用这些功能来完成任务,满足业务需求。如“系统应该发送电子邮件来通知用户已接受其预定”。 +- **系统需求**(system requirement)用于描述包含多个子系统的产品(即系统)的顶级需求。系统可以只包含软件系统,也可以既包含软件又包含硬件子系统 +- **非功能需求** 为满足用户业务需求而必须具有且除功能需求以外的特性 + - 安全性 + - 可靠性 + - 易用性 + - 可维护性 + - 可移植性 + +### 需求开发 + + + +需求开发活动包括以下几个方面: + +1. 确定用户分类 +2. 获取每类用户的需求 +3. 了解实际用户任务和目标 +4. 分析源于用户的信息以获取用户任务需求、功能需求、业务规则、质量属性、建议解决方法和附加信息 +5. 将系统级的需求分为几个子系统 +6. 了解相关质量属性的重要性 +7. 商讨实施优先级的划分 +8. 编写成规格说明和模型。 +9. 评审需求规格说明 + + +### 用例描述 + +用例图描述了参与者要求系统能“做什么”,但是缺乏描述系统该“怎么做”的细节。一般情况下,每个用例应具有一个用例描述。 + +#### 用例描述说明 + + + +- **用例名称**:用例名称应该表明用户的意图或用例的用途,例如:借阅图书、归还图书、预定图书等。 +- **用例编号**: UC 0001 +- **简要说明**:对用例进行简要说明,描述该用例的作用,说明应当简明扼要。 +- **参与者**:与此用例相关的参与者列表 +- **前置条件**:执行用例之前系统必须满足的条件,例如:当学生借阅图书时,借阅图书用例需要获取学生的借阅证信息,如果学生使用了一个已经被注销的借阅证,那么借阅图书用例就不能执行。 +- **后置条件**:后置条件将在用例成功完成以后得到满足,它提供了系统的部分描述。例如:当学生借阅图书成功后,借阅图书用例应该提供该学生的所有借阅信息。 +- **基本操作流程**:指参与者在用例中所遵循的主逻辑路径。例如,借阅图书用例的基本操作流程如下: + - (1) 图书管理员输入借阅证信息 + - (2) 系统检查读者是否有超期的借阅信息 + - (3) 系统检查读者的借书数量是否已经达到借书限额 + - (4) 图书管理员输入要借阅的图书信息 + - (5) 系统将读者的借阅信息保存到数据库中 +- **可选操作流程**:指参与者在用例中所遵循的次逻辑路径,通常是指出现异常或发生错误的情况下所遵循的路径。 +- **涉及数据**:填写该用例涉及的相关信息,如图书名字,价格,ISBN号,出版日期等 + +#### 完整用例示例 + + + +| 主执行者 | 请求者 | +| --------- | ---------------------------------------- | +| 语境中的目标 | 请求者通过系统买东西,并得到说买的东西。不包括付款方面的内容。 | +| 范围 | 业务——整个购买机制,包括电子的和非电子的,正如人们在公司中说见到的一样。 | +| 层次 | 概要 | +| 项目相关人员和利益 | 请求者:希望得到她订购的东西,并且操作要简单。公司:希望控制花费,但允许必要的购买。供货商:希望得到任何已发货物的货款。 | +| 前置条件 | 无 | +| 最小保证 | 每一个发出的订单都已经获得有效认证者的许可。订单具有可跟踪性,以便公司只对收到的有效货物开账单。 | +| 成功保证 | 求者得到货物,修改预算,记入借方。 | +| 触发事件 | 请求者决定买东西。 | +| 主成功场景 | 1. 请求者:发起一个请求。2. 批准者:检查预算中的资金,检查货物的价格,完成提交请求。3. 买者:检查仓库的存货,找出最好的供货商。4. 认证者:验证批准者的签名。5. 买者:完成订购请求,向供货商发出PO(订单)。6. 供货商:把货物发送给接收者,得到发货收据(这一点超出了本系统的设计范围)。7. 接收者:记录发货情况;向请求者发送货物。8. 请求者:设置请求已被满足标志。 | +| 扩展 | 1a)请求者不知道供货商和货物价格:不填写这些内容,然后继续。1b)在收到货物之前的任意时刻,请求者都可以修改或取消请求:如果取消,则把这个请求从执行处理中取消。(从系统中删除吗?)如果降低价格,则不影响其处理过程。如果提高价格,则把请求送回批准者。2a)批准者不知道供货商或货物价格:不填写这些内容,留待买者填写或返回。2b)批准者不是请求者的经理:只是批准者签名仍然可行。2c)批准者拒绝申请:送回给请求者,要其修改或删除。3a)买者在仓库中找到货物:将存货先发出,并从申请者要求的总购买者中减去已经发出的这部分货物量,然后继续。3b)买者填写在前面活动中没有填写的供货商和价格信息:请求重新发回给批准者。4a)认证者拒绝批准者:发回请求者,并将此请求从执行处理中取消。5a)请求涉及到多个供货商:买者创建多个PO5b)买者将多个请求合并:相同的过程,但是用被合并的请求标记PO6a)供货商没有按时发货:系统发出没有发货警告。7a)部分发货:接收者在PO上做部分发货标记,然后继续。7b)多个请求PO的部分发货:接收者给每个请求分配货物数量,然后继续。8a)货物不对或质量不合格:请求者拒绝接收所发送的货物。8b)请求者已经离开公司:买者同请求者的经理进行核实,或者重新指派申请者,或者返还货物并取消请求。 | +| 技术和数据变动列表 | 无 | +| 优先级 | 多种 | +| 发行版本 | 几个 | +| 响应时间 | 多样 | +| 使用频率 | 3/天 | +| 主执行者的渠道 | 网络浏览器、邮件系统或类似系统 | +| 次要执行者 | 供货商 | +| 次要执行者的渠道 | 传真、电话或汽车 | +| 未解决的问题 | 什么时候从系统中删除被取消的请求?要取消一个请求需要那些权限?谁能修改一个请求的内容?请求中需要保留哪些修改历史记录?当请求者拒绝已经发送的货物时,会发生什么情况?申请和订货在运作上有什么不同? 订购如何参考和使用内部存货? | + +#### 用例执行步骤的10大准则 + +(1)使用简单的语法; +句子结构应该非常简单:主语……谓语动词……直接宾语……前置短语 +例如 系统……从帐户余额中扣除……一定数量…… + +(2)明确地写出“谁控制球”; +作者举了踢足球的场景的例子,说明了不管步骤的执行者如何变化,都要遵循(1)描述的格式。 + +(3)从俯视的角度来编写用例; +从用户的角度来写用例,而不是从系统内部来描述系统 + +~~(4)显示过程向前推移;~~ + +(5)显示执行者的意图而不是动作; +通过操纵系统的用户界面来描述用户的动作,这是在编写用例时常见的一种严重错误,它使得编写的目标处于一个很低的层次。我把它叫做“界面细节描述(interface detail description)”。在需求文档中,我们只关心界面所要达到的意图,总结在执行者之间传递的信息。可将这些低层次的步骤合并成一个步骤。 + +~~(6)包含“合理”的活动集;~~ + +(7)“确认”而不是“检查是否” +这是一个经常犯的错误,写用例不是写程序流程,不需要用选择语法,需要选择的时候,在扩展场景里体现 + +(8)可选择地提及时间限制; + +(9)习惯用语:“用户让系统A与系统B交互”; +要分开来写,用户与系统A怎么怎么样,然后系统A和系统B怎么怎么样,这样用户才能看的懂。 + +(10)习惯用语:“循环执行步骤X到Y,直到条件满足”; +同(7),但如果需要重复的话,可直接在重复的步骤的前面和后面说明即可。 + +总之,这10大原则,目的就是为了让用例成为用户和开发人员沟通的桥梁,所以语言要简单易懂,而且要逻辑清晰。 + +### 参考链接 + + + +- [需求入门: 需求工程=需求开发+需求管理](http://www.uml.org.cn/RequirementProject/201005285.asp) +- [软件需求规格说明书模板](https://jingyan.baidu.com/article/6dad5075eae10da123e36e80.html) +- [描述用例](http://blog.csdn.net/wlanye/article/details/7445676) +- [软件需求3个层次――业务需求、用户需求和功能需求](https://www.cnblogs.com/litian/articles/2047981.html) +- [非功能性六大点](https://jingyan.baidu.com/article/90bc8fc80960f1f653640ce0.html) +- [《编写有效用例》学习笔记](http://lib.csdn.net/article/softwaretest/24322) + + + + + +## 文档 + +### 文档列表 + +1. 可行性分析报告 +2. 项目开发计划 +3. **软件需求说明书** +4. **概要设计说明书** +5. 详细设计说明书 +6. **用户操作手册、运维部署文档** +7. 测试计划 +8. 测试分析报告 +9. 开发进度月报 +10. 项目开发总结报告 +11. 软件维护手册 +12. 软件问题报告 +13. 软件修改报告 + +### 需求分析文档 + +内容 + +- 项目背景 +- 使用角色 +- 功能需求 + - 功能划分 + - 用例图 + - 用例描述 +- 性能需求 + - 用户数评估 + - 响应时间要求 + - 可靠性需求 +- 用户界面 + +### 概要设计文档 + +对大部分的公司来说,概要设计文档是唯一的设计文档,对后面的开发、测试、实施、维护工作起到关键性的影响。 + +内容 + +- 功能介绍 + - 用例图 +- 整体架构 + - 层次结构 + - 模块划分 + - 模块间调用关系 + - 包图、组件图 +- 接口设计 + - 对外接口草案 + - 模块间接口草案 +- 模块设计 + - 职责描述 + - 类图 + - 算法描述(如有) +- 数据流设计 + - 流程图 + - 序列图 + - 状态图 +- 数据库概要设计 + - 表设计 + - 核心语句 +- 主要界面 +- 部署结构 + - 部署图 +- 非功能需求 + - 性能需求 + - 安全需求 + - 扩展性需求 +- 编码规范 + - 代码风格 + - 数据库命名规范 + - 接口规范 + +### 详细设计文档 + +同概要设计文档,只是需要更详细,比如 + +- 类图要精确到字段,方法的类型, +- 数据库设计要精确到字段类型,索引设计 +- 核心算法要画出序列图及伪代码 + +### 运维部署文档 + +内容 + +- 硬件需求 +- 软件需求 +- 外部依赖 +- 部署步骤 +- 配置说明 +- 维护命令:启动、停止、重启 +- 监控指标 +- 如何升级 +- 常见问题处理 +- 数据备份 + +### 参考链接 + +- [软件开发文档范例](http://zz563143188.iteye.com/blog/1835305) +- [概要设计文档编写规范](http://blog.csdn.net/nengyu/article/details/3758312) + +# 工具 + +## 其它 + +- [f.lux - 全天候保护眼睛健康软件!自动调整屏幕色温减少蓝光防疲劳,长时间玩电脑必备!](https://www.iplaysoft.com/flux.html) +- [MarkDown 写 ppt](https://yhatt.github.io/marp/) +- [在线根据 markdown 生成 ppt](http://www.vmfor.com/ppt/index.html) + +## 编辑器 + + + +### 功能需求 + + + +- 行号显示 +- 语法高亮 +- 自动识别编码 +- 自动缩进 +- 智能提示 +- 列编辑 +- 代码片段 +- 代码折叠 +- 新建文件模板 +- 多标签 +- 会话管理 +- 分屏 +- 书签 +- 多次光标点跳转 + +### VS Code + +快捷键 + +- 注释切换:ctrl +/ +- 格式化代码: Alt + Shift + F +- 自动换行:Alt + Z +- 选区移动:Alt + ↑/↓ +- 多选修改:Ctrl + D + +### Eclipse + +- 格式化代码:Ctrl + Shift + F + +参考链接: + +- [为什么我选择使用 VS Code进行前端开发?](https://zhuanlan.zhihu.com/p/28631442) +- [TextMate代码片段语法](https://manual.macromates.com/en/snippets) + +# 物联网 + +## 参考链接 + +[关于RS232 和 RS485 的区别](http://blog.csdn.net/foreverhuylee/article/details/23375079) + +## 硬件技术 + + +### 串口 + +串行接口(Serial port)又称“串口”,主要用于串行式逐位数据传输。 + +串行接口按电气标准及协议来分,包括RS-232-C、RS-422、RS485、USB等。 + +- RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。 +- USB是近几年发展起来的新型接口标准,主要应用于高速数据传输领域。 + +**RS-232-C :也称标准串口**,是目前最常用的一种串行通讯接口。电脑一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。 + +**RS-485** :为扩展应用范围,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围。 + +**Universal Serial Bus(通用串行总线) :简称USB,** USB接口是电脑主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,新的USB 2.0可达480Mbps; + +**RJ-45接口** :是以太网最为常用的接口, 8个位置(8针)的模块化插孔或者插头 + + + +串口属性: + +- PortName 串口名 +- BaudRate 获取或设置串行波特率bit/s 默认值9600 +- DataBits 获取或设置每个字节的标准数据位长度 默认值8 +- StopBits 获取或设置每个字节的标准停止位数 默认值One +- Parity 获取或设置奇偶校验检查协议 默认值None + + + +参考 + +- [C#中的串口通信](http://www.cnblogs.com/51net/p/6050840.html) + +### IC卡,ID卡,M1卡,射频卡的区别 + +IC卡全称集成电路卡(Integrated Circuit Card),又称智能卡(Smart Card)。可读写,容量大,有加密功能,数据记录可靠,使用更方便,如一卡通系统、消费系统等 + +ID卡全称身份识别卡(Identification Card),是一种不可写入的感应卡,含固定的编号, + +[门禁卡是选择IC卡好还是ID卡好](https://jingyan.baidu.com/article/54b6b9c0d8056f2d593b474c.html) + + + + +### RFID + +从概念上来讲,RFID类似于[条码扫描](https://baike.baidu.com/item/%E6%9D%A1%E7%A0%81%E6%89%AB%E6%8F%8F),对于条码技术而言,它是将已编码的条形码附着于目标物并使用专用的扫描读写器利用光信号将信息由条形磁传送到扫描读写器;而RFID则使用专用的RFID读写器及专门的可附着于目标物的RFID标签,利用频率信号将信息由RFID标签传送至RFID读写器。 + +[射频识别系统](https://baike.baidu.com/item/%E5%B0%84%E9%A2%91%E8%AF%86%E5%88%AB%E7%B3%BB%E7%BB%9F)最重要的优点是非接触识别,它能穿透雪、雾、冰、[涂料](https://baike.baidu.com/item/%E6%B6%82%E6%96%99)、[尘垢](https://baike.baidu.com/item/%E5%B0%98%E5%9E%A2)和条形码无法使用的恶劣环境阅读标签,并且阅读速度极快,大多数情况下不到100毫秒。 + +一维条形码的容量是50Bytes,二维条形码最大的容量可储存2至3000字符,RFID最大的容量则有数MegaBytes. + +现今的条形码印刷上去之后就无法更改,RFID标签则可以重复地新增、修改、删除RFID卷标内储存的数据,方便信息的更新。 + +### NFC + +NFC近场通信技术是由非接触式[射频识别](https://baike.baidu.com/item/%E5%B0%84%E9%A2%91%E8%AF%86%E5%88%AB)([RFID](https://baike.baidu.com/item/RFID))及互联互通技术整合演变而来,在单一芯片上结合感应式[读卡器](https://baike.baidu.com/item/%E8%AF%BB%E5%8D%A1%E5%99%A8)、感应式卡片和点对点的功能,能在短距离内与兼容设备进行识别和[数据交换](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E4%BA%A4%E6%8D%A2)。 + +NFC手机内置NFC芯片,比原先仅作为标签使用的RFID更增加了数据双向传送的功能,这个进步使得其更加适合用于电子货币支付的; + +NFC传输范围比RFID小,RFID的传输范围可以达到几米、甚至几十米,但由于NFC采取了独特的信号衰减技术,相对于RFID来说NFC具有距离近、带宽高、能耗低等特点。 + +应用方向不同。NFC看更多的是针对于消费类电子设备相互通讯,有源RFID则更擅长在长距离识别。 + +NFC的短距离通信特性正是其优点,由于耗电量低、一次只和一台机器链接,拥有较高的保密性与安全性,NFC有利于信用卡交易时避免被盗用。 + +### PLC + +### 继电器 + +继电器是具有隔离功能的自动开关元件,广泛应用于遥控、遥测、通讯、自动控制、机电一体化及电力电子设备中,是最重要的控制元件之一。 + +继电器一般都有能反映一定输入变量(如电流、电压、功率、阻抗、频率、温度、压力、速度、光等)的感应机构(输入部分);有能对被控电路实现“通”、“断”控制的执行机构(输出部分);在继电器的输入部分和输出部分之间,还有对输入量进行耦合隔离,功能处理和对输出部分进行驱动的中间机构(驱动部分)。 + +### 二进制协议 + +二进制常用操作 + +``` + + int value = 170; + void print(int x) => Console.WriteLine(Convert.ToString(x, 2).PadLeft(8, '0')); + int make_mask(int x) => 1 << x; + int set(int x, int i) => x | make_mask(i); + int unset(int x, int i) => x & ~make_mask(i); + bool isset(int x, int i) => (x & make_mask(i)) != 0; + + //170的二进制显示: 10101010 + print(value); + + // 右数第 5 位置 1 : 10111010 + print(set(value, 4)); + + // 右数第 4 位置 0: 10100010 + print(unset(value, 3)); + + // 右数第 4 位是否为1: true + Console.WriteLine(isset(value, 3)); + + //右数第 3 位是否为1: false + Console.WriteLine(isset(value, 2)); +``` + +### 其它 + +TTL电平信号之所以被广泛使用,原因是:通常我们采用二进制来表示数据。而且规定,+5V等价于逻辑“1”,0V等价于逻辑“0”。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。 + +GND是电线接地端的简写。代表地线或0线。这个地并不是真正意义上的地,是出于应用而假设的一个地,对于电源来说,它就是一个电源的负极。 + +VCC:电源电压(双极器件);电源电压(74系列数字电路); + +RXD 为接收数据的引脚,TXD 为发送数据的引脚。 + +DTE提供或接收数据,连接到调制解调器上的计算机就是一种DTE。DTE提供或接收数据,连接到网络中的用户端机器,主要是计算机和终端设备。 + +在网络端的连接设备称为DCE(Data-Communication Equipment)。DTE与进行信令处理的DCE相连。 +DTE通过DCE设备,例如,调制解调器,连接到数据网络。 + +RS232标准中的RTS与CTS:即请求发送/清除发送,用于半双工时的收发切换,属于辅助流控信号。半双工的意思是说,发的时候不收,收的时候不发。那么怎么区分收发呢?缺省时是DCE向DTE发送数据,当DTE决定向DCE发数据时,先有效RTS,表示DTE希望向DCE发送。一般DCE不能马上转换收发状态,DTE就通过监测CTS是否有效来判断可否发送,这样避免了DTE在DCE未准备好时发送所导致的数据丢失。 + +差分输入的是将两个输入端的差值作为信号,这样可以免去一些误差,比如你输入一个1V的信号电源有偏差,比实际输入要大0.1.就可以用差分输入1V和2V一减就把两端共有的那0.1误差剪掉了。单端输入无法去除这类误差。 + +在某些系统里,系统'地'被用作电压基准点。当'地'当作电压测量基准时,这种信号规划被称之为单端的。 + +差分信号的第一个好处是,因为你在控制'基准'电压,所以能够很容易地识别小信号。 +差分信号的第二个主要好处是,它对外部电磁干扰(EMI)是高度免疫的。 +差分信号提供的第三个好处是,在一个单电源系统,能够从容精确地处理'双极'信号。 + +上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用。下拉同理,也是将不确定的信号通过一个电阻钳位在低电平。 + +# 企业信息化 + +大多数OA产品功能集中在信息共享、行政办公领域,一些主流OA系统虽然引入了工作流,但相对比较封闭,开放性和扩展性不够。 + +BPM是一个开放性平台,不仅能实现所有OA的功能,还能满足企业内部系统之间集成的需求,在BPM引擎驱动下,企业的流程终会形成一个闭环。 + +ERP + +WMS + +MES + +ESB + +SOA + +- [智能MES解决方案](http://www.rtdsoft.com/channels/57.html) +- [制造执行系统(MES)选型与实施指南简版](https://wenku.baidu.com/view/052b5ef4a32d7375a41780c8.html) +- [OpenMES架构说明书](https://wenku.baidu.com/view/2a98711ec281e53a5802ffc8.html) + +## 快速开发框架 + +- 登录注册: Apache Shiro +- 组织机构 +- 权限管理: Apache Shiro +- 增删改查 +- 后台界面 +- 菜单管理 +- 工作流:Activity +- 报表:JasperReports + +参考 + +- http://www.jeecg.org/ +- [Java通用权限系统管理(Spring+springMVC+ibatis+Angularjs)](http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/liaodehong/article/details/53100313) +- [组织机构对象模型设计及实现](http://blog.csdn.net/wangpeng047/article/details/7280800) +- [LigerUI 快速开发UI框架](http://www.ligerui.com/) +- https://github.com/thinkgem/jeesite + +jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块 +http://blog.csdn.net/qing_gee/article/details/76223064 + + +# 未整理 + +[Avoid calling Invoke when the control is disposed](https://stackoverflow.com/questions/1874728/avoid-calling-invoke-when-the-control-is-disposed) + + + +http://blog.csdn.net/pkueecser/article/details/50610796 时间序列数据库的秘密 + + + +https://github.com/justjavac/ReplaceGoogleCDN Replace Google CDN + +https://stackoverflow.com/questions/31572580/how-covert-c-sharp-datetime-to-java-datetimeusing-joda-time +https://pine.fm/LearnToProgram/ +http://www.qdaily.com/articles/42060.html +https://zh.wikihow.com/%E5%AD%A6%E4%B9%A0%E7%BC%96%E7%A8%8B + +nginx waf + +前端系列教程 +https://chuanke.baidu.com/s5508922.html +http://growth.phodal.com/ + +关于PHP程序员解决问题的能力 +http://www.cnblogs.com/phpworld/p/8038581.html +ZH奶酪:编程语言入门经典100例【Python版】 +http://www.cnblogs.com/CheeseZH/archive/2012/11/05/2755107.html +https://www.coursera.org/learn/python + +JavaScript专题之惰性函数 https://segmentfault.com/a/1190000010783034 +关于尾递归的问题 https://segmentfault.com/q/1010000002705723 + +var pi = 3.14; +function area(r) { return 2 * pi * r;} +function iseven(n) {return n % 2 == 0;} + +function range(n, m) { console.log(n); (n < m) ? range(n + 1, m) : null;} +function rangef(n, m, f) { f(n); (n < m) ? rangef(n + 1, m, f) : null;} +function rangesum(n, m, sum) { return n < m ? n + rangesum(n + 1, m, sum) : n + sum; } +function rangesumf(n, m, sum, f) { return n < m ? f(n) + rangesumf(n + 1, m, sum, f) : f(n) + sum; } +function rangecond(n, m, cond) { cond(n) ? console.log(n) : null; (n < m) ? rangecond(n + 1, m, cond) : null;} + +7 of the Best Code Playgrounds +https://www.sitepoint.com/7-code-playgrounds/ + +Beginning Programming For Dummies +https://www.amazon.com/Beginning-Programming-Dummies-Wallace-Wang/dp/0470088702 +jQuery File Upload跨域上传 +https://www.cnblogs.com/ZHF/p/5057416.html +Javascript知识点:IIFE - 立即调用函数 +https://linghucong.js.org/2016/04/25/2016-04-08-Javascript-IIFE/ +技术面试需要掌握的基础知识 +https://github.com/CyC2018/Interview-Notebook + +### 函数组合 +function rangei(n) { + var i = 0; + return function() { + var ret = i < n ? i : null; + i = i + 1; + return ret; + }; +} + +function mapi(i, f) { + return function () { + var t = i(); + return t == null ? null : f(t); + }; +} + +function filteri(i, c) { + return function inner() { + var t = i(); + return t == null ? null : c(t) ? t : inner(); + }; +} + +function reducei(i, f, init_val) { + var t = i(); + return t == null ? init_val : reducei(i, f, f(init_val, t)); +} + +function iseven(x) { + return x % 2 == 0; +} + +function sqr(x) { + return x * x; +} + +function add(x, y) { + return x + y; +} + +//10 以内偶数的平方和 +reducei(mapi(filteri(rangei(10), iseven), sqr), add, 0) + +Jquery mobile change page +https://stackoverflow.com/questions/9738948/jquery-mobile-change-page +The Truth About Multiple H1 Tags in the HTML5 Era +https://webdesign.tutsplus.com/articles/the-truth-about-multiple-h1-tags-in-the-html5-era--webdesign-16824 + +How to set up Spark on Windows? +https://stackoverflow.com/questions/25481325/how-to-set-up-spark-on-windows + + +Git for windows 中文乱码解决方案 +https://segmentfault.com/a/1190000000578037 +Best way to find if an item is in a JavaScript array? [duplicate] +https://stackoverflow.com/questions/143847/best-way-to-find-if-an-item-is-in-a-javascript-array +后台管理UI的选择 +https://www.cnblogs.com/webenh/p/5815732.html + +配置 PHP 错误日志 + +mkdir /data/logs/php +chown apache:apache /data/logs/php + +php-fpm.conf: + + [global] + ; php-fpm pid文件 + pid = /usr/local/php/var/run/php-fpm.pid + ; php-fpm 错误日志路径 + error_log = /data/logs/php/error.log + ; php-fpm 记录错误日志等级 + log_level = notice + [www] + ; 记录错误到php-fpm的日志中 + ;catch_workers_output = yes + ; 慢日志 + slowlog = /data/logs/php/www-slow.log + ; 关闭打印日志 + php_flag[display_errors] = off + ; 错误日志 + php_admin_value[error_log] = /data/logs/php/www-error.log + ; 记录错误 + php_admin_flag[log_errors] = on + ; 内存使用量 + php_admin_value[memory_limit] = 32M + +php.ini: + + ; 错误日志 + log_errors = On + ; 显示错误 + display_errors = Off + ; 日志路径 + error_log = "/usr/local/lnmp/php/var/log/error_log" + ; 错误等级 + error_reporting = E_ALL&~E_NOTICE + + +nginx 路径匹配测试 + + location /test { + add_header Content-Type text/html; + return 200 'hello'; + } + +nginx php 测试 + + chmod o+x /root + chmod o+x /root/haohu + chmod o+x /root/haohu/phptest + + location ~ /test$ { + fastcgi_pass 127.0.0.1:9000; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /root/haohu/phptest/test.php; + } + +nginx 日志格式 + + log_format main '$time_iso8601 $status $request_time $upstream_response_time $remote_addr ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + +日志滚动 + + /var/log/nginx/*.log + /data/log/nginx/*.log + /data/logs/techaction/*.php + /data/logs/php/*.log + /var/log/php-fpm/*log + { + daily + rotate 30 + missingok + notifempty + compress + sharedscripts + + postrotate + /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true + /bin/kill -SIGUSR1 `cat /var/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true + endscript + } + + +Linux日志文件总管——logrotate +https://linux.cn/article-4126-1.html +Linux中find常见用法示例 +http://www.cnblogs.com/wanqieddy/archive/2011/06/09/2076785.html + +找到 /data/logs 目录下所有 15 天之前修改过的 .php 和 .log 文件删除掉,删除前加确认,去掉确认的话,把 -ok 改成 -exec +find /data/logs/ -type f \( -name '*.php' -o -name '*.log' \) -mtime +15 -print -ok rm {} \; + + + +防止文件误删:http://www.cnblogs.com/lihaozy/archive/2012/08/17/2643784.html + + mkdir -p ~/.trash + alias rm=trash + alias r=trash + alias rl='ls ~/.trash' + alias ur=undelfile + + undelfile() + { + mv -i ~/.trash/$@ ./ + } + + trash() + { + mv $@ ~/.trash/ + } + + cleartrash() + { + read -p "clear sure?[n]" confirm + [ $confirm == 'y' ] || [ $confirm == 'Y' ] && /usr/bin/rm -rf ~/.trash/* + } + +[译] Node.js 8: util.promisify() +https://segmentfault.com/a/1190000009743481 +Node.js Async Best Practices & Avoiding the Callback Hell +https://blog.risingstack.com/node-js-async-best-practices-avoiding-callback-hell-node-js-at-scale/ +Nodejs 中使用 Async/Await +https://www.jianshu.com/p/0837dde8dcd5 + +promisify + async + + (async () => { + const fs = require('fs'); + const util = require('util'); + + const readFile = util.promisify(fs.readFile); + + const txt = await readFile('./notes.txt'); + console.log(txt); + })(); + +好吧,CSS3 3D transform变换,不过如此! +http://www.zhangxinxu.com/wordpress/2012/09/css3-3d-transform-perspective-animate-transition/ +客栈说书:CSS遮罩CSS3 mask/masks详细介绍 +http://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/ +新手应该如何学习 PHP 语言? +https://www.zhihu.com/question/20003635/answer/338733500 +目前最详细的PHP培训课程安排 +http://baijiahao.baidu.com/s?id=1563138980186749&wfr=spider&for=pc + +CSS3 box-pack 属性 +http://www.w3school.com.cn/cssref/pr_box-pack.asp +设计一个灵活的、可维护CSS和SVG饼图SVG +https://www.jianshu.com/p/f6526355de54 +SVG中stroke-dasharray及stroke-dashoffset属性 +https://blog.csdn.net/u014291497/article/details/78409350 +纯CSS实现帅气的SVG路径描边动画效果 +http://www.zhangxinxu.com/wordpress/2014/04/animateion-line-drawing-svg-path-%E5%8A%A8%E7%94%BB-%E8%B7%AF%E5%BE%84/ + +https://secbone.com/ +https://github.com/Secbone + +# 快速学习一门语言 +- 打字练习: + - 数字,字母,下划线 + - 标点符号:+-*/\%(){};"',.!<>[]?^| +- 输出 + - 打印字符串 + - 打印数字 + - 混合打印 + - 输出复杂信息 + - 格式化输出 +- 字符串连接 +- 数学运算: + - * / % +- 关系运算: > < == != >= <= +- 逻辑运算: && || ! +- 变量和赋值: = +- 条件语句和关系操作符:两个数谁最大 +- 多重条件语句和逻辑操作符:三个数谁最大 +- 循环语句:打印 5 次 Hello +- for 循环:打印 10 以内的偶数 +- 数组基本操作 + - 打印数组 + - 获取数组长度 + - 获取指定索引元素 + - 修改指定索引元素的值 + - 数组末尾增加元素 + - 某元素是否存在 + - 遍历数组 + - 删除指定索引元素 + - 重排数组 + - 任意位置插入元素 + - 连接成字符串 + - 练习 + - 找出一个数组中最大的数 + - 找出一个数组中最大的奇数 + - 求出数组中所有奇数的和 +- 字符串基本操作 + - 字符串长度 + - 字符串连接 + - 转换大小写 + - 去掉首尾指定字符 + - 用某字符在首尾填充 + - 分割成数组 + - 比较两个字符串 + - 获取某子串的位置 + - 是否存在某个子串 + - 替换某个子串 + - 获取子串 + - 是否以某子串开头或结尾 + - 练习 + - 判断某字符是否为大写 + - 把字符串中的每个单词首字母大写 + - 按大写字母分割字符串 +- 字典基本操作 + - 根据键获取值 + - 添加键值对 + - 修改键值对 + - 删除指定键 + - 某键是否存在 + - 遍历字典 +- 时间操作 + - 设置时区 + - 获取当前时间的时间戳 + - 获取当前时间格式化字符串 +- 文件操作:打开并逐行读取文件 +- 类型判断 + - 是否为数字 + - 是否为整型 + - 是否为浮点数 + - 是否为字符串 + - 是否为 null + - 是否为 数组 + - 是否为空 +- 类型转换 + - 数字转字符串 + - 字符串转数字 +- 函数 + - 函数定义和使用 + - 匿名函数和闭包 + - 返回函数的函数 + - 参数为函数的函数 + - 函数的递归调用 +- 面向对象 + - 类 + - 方法 + - 字段 + - 静态字段 + - 子类 + +数组: +- get set remove insert length +- push pop shift unshit slice indexof +- startswitch endswitch contains +- map filter reduce +- removeCond groupby sortby countby + +字典 +- add set get remove +树 + +输入输出文件 + +Android Studio: + +- 下载带 Sdk 版本 +- 修改字体 +- 修改编码 +- 自动提示快捷键修改 +- 去掉拼写检查 +- 设置 git 目录 +- 禁用不需要的插件 +- 自动导入 +- File –> Other Settings –> Default Project Structure +- idea.properties disable.android.first.run=true +- ANDROID_SDK_HOME 环境变量指向 D:\android-home 把 .android 目录拷过去 +- 禁止自动打开上次的工程 +- 禁止代码折叠 + +Android: + +- 加载中效果 +- 短暂提示 +- 网络访问要用AsyncTask +- 小图标 +- 全局错误挂接 +- 优雅退出 +- 打日志 +- 每页的标题 +- 返回按钮 +- 导航条一直存在 + + +安卓异步任务类AsyncTask——突出一个简单、好用 +https://blog.csdn.net/jerrycqu/article/details/49357191 +Android OkHttp的基本用法 +https://www.jianshu.com/p/c478d7a20d03 +Toolbar的简单使用 +https://blog.csdn.net/monalisatearr/article/details/78415585 +ListView中自定义adapter的封装 +https://www.cnblogs.com/smyhvae/p/4477079.html +Gson解析——从简单数据到复杂数据 +https://www.jianshu.com/p/886f7b7fca7d +LoadingBar - 如何更优雅的使用Loading +https://blog.csdn.net/aa464971/article/details/70197394 +NavigationView的使用 +https://blog.csdn.net/bskfnvjtlyzmv867/article/details/70245826 +Android中处理崩溃异常 +https://blog.csdn.net/liuhe688/article/details/6584143 + + + + +Android Studio 入门级教程( +https://www.cnblogs.com/abao0/archive/2017/06/02/6934023.html + +详解 Android 的 Activity 组件 +https://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/ +Android之自定义Adapter的ListView +http://www.cnblogs.com/topcoderliu/archive/2011/05/07/2039862.html +想写个app,在哪里可以找到icon素材? +https://www.zhihu.com/question/40639915?sort=created +https://github.com/konifar/android-material-design-icon-generator-plugin +Android学习 - 美化ListView +https://blog.csdn.net/wolflz/article/details/45078107 +Android开发之漂亮Button样式 +https://www.jianshu.com/p/e5e8a98fc5d9 + + +图标制作 +https://www.iconfinder.com/editor/ +https://www.flaticon.com/free-icons/programming-language/2 +https://glyphter.com/ +https://www.shutterstock.com/zh/image-vector/workplace-programmer-coder-desktop-pc-laptop-705161689?src=Kd61QRsGtq1hI9vEL7LU2w-1-49 +https://iconsflow.com/editor + +APP第三方微信登录与公众号数据打通 +https://www.jianshu.com/p/18b1288f4c41 + +Flex 布局教程:语法篇 +http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html +Flex 布局教程:实例篇 +http://www.ruanyifeng.com/blog/2015/07/flex-examples.html + +Programmed Lessons in QBasic +http://chortle.ccsu.edu/QBasic/index.html +中等职业教育国家规划教材·编程语言基础:QBASIC语言(计算机及应用专业) +https://item.jd.com/10493424.html + +wget https://npm.taobao.org/mirrors/node/v8.9.3/node-v8.9.3-linux-x64.tar.xz + +tar -xzvf node-v8.9.3-linux-x64.tar.gz +tar -xvf node-v8.9.3-linux-x64.tar +ln -s /root/node-v8.9.3-linux-x64/bin/node /usr/local/bin/node +ln -s /root/node-v8.9.3-linux-x64/bin/npm /usr/local/bin/npm +npm -v +npm install -g cnpm --registry=https://registry.npm.taobao.org + +$ sudo npm install forever -g #安装 +$ forever start app.js #启动 +$ forever stop app.js #关闭 +$ forever start -l forever.log -o out.log -e err.log app.js #输出日志和错误 + + +## Apache backend for www.quancha.cn ## +upstream apachephp { + server ip:8080; #Apache +} + +## Start www.quancha.cn ## +server { + listen 80; + server_name www.quancha.cn; + + access_log logs/quancha.access.log main; + error_log logs/quancha.error.log; + root html; + index index.html index.htm index.php; + + ## send request back to apache ## + location / { + proxy_pass http://apachephp; + + #Proxy Settings + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_max_temp_file_size 0; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + } +} + +How to easily convert utf8 tables to utf8mb4 in MySQL 5.5 +https://dba.stackexchange.com/questions/8239/how-to-easily-convert-utf8-tables-to-utf8mb4-in-mysql-5-5 + +不翻墙也能找到免费优质素材,7个免费商用素材库墙裂推荐 +https://zhuanlan.zhihu.com/p/28508804 + +微服务分布式事务Saga模式简介 +http://www.jdon.com/49307 + +大数据告诉你:为啥近5年来Python如此火爆? +https://www.sohu.com/a/196290953_704222 +kvm管理平台webvirtmgr的部署 +https://www.jianshu.com/p/160272d81ac3 +Comfortable interface for KVM? (with PCIe Passthrough) +https://www.centos.org/forums/viewtopic.php?t=52640 + +CentOS之7与6的区别 +https://www.cnblogs.com/Csir/p/6746667.html +开源虚拟化管理平台Ovirt简介和配置环境搭建 +https://www.2cto.com/os/201202/120678.html +libvirt apps +https://libvirt.org/apps.html#web +十分钟带你理解Kubernetes核心概念 +http://dockone.io/article/932 +使用 Docker/LXC 迅速启动一个桌面系统 +https://www.oschina.net/question/54100_137626 +在Docker中运行桌面应用 +https://yq.aliyun.com/articles/224645# +docker-desktop +https://github.com/rogaha/docker-desktop +图形化界面的 docker ? +https://www.zhihu.com/question/34493859 +Windows Docker 安装 +http://www.runoob.com/docker/windows-docker-install.html +https://blog.csdn.net/tina_ttl/article/details/51372604 +后端架构师技术图谱 +https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84 + +termtosvg +A Linux terminal recorder written in Python that renders your command line sessions as standalone SVG animations. +https://github.com/nbedos/termtosvg + +如何编写技术解决方案 +https://wenku.baidu.com/view/b42e31a1760bf78a6529647d27284b73f2423635.html?from=search + +p5.js +http://ofcourse.io/biao-ti-3/ + +腾讯云 使用DockerHub加速器 +https://cloud.tencent.com/document/product/457/9113 +理解Docker(7):Docker 存储 - AUFS +https://www.cnblogs.com/sammyliu/p/5931383.html +[授权发表]基于 ssh + Xpra 构建 Docker 桌面系统 +https://blog.csdn.net/tinylab/article/details/45443563 +CentOS6下docker的安装和使用 +https://www.cnblogs.com/zhangzhen894095789/p/6641981.html?utm_source=itdadao&utm_medium=referral + +How to stop docker pull +https://stackoverflow.com/questions/29486032/how-to-stop-docker-pull +DOCKER_OPTS do not work in config file /etc/default/docker +https://stackoverflow.com/questions/27763340/docker-opts-do-not-work-in-config-file-etc-default-docker + +Set Docker_Opts in centos +https://stackoverflow.com/questions/26166550/set-docker-opts-in-centos + +vi /etc/sysconfig/docker + OPTIONS=--registry-mirror=https://mirror.ccs.tencentyun.com +sudo service docker restart + +netstat -tnpl +CONTAINER_ID=$(sudo docker run -d -p 2222:22 rogaha/docker-desktop) + +echo $(sudo docker logs $CONTAINER_ID | sed -n 1p) +User: docker Password: eengoch3ooK5 +docker port $CONTAINER_ID 22 + +/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT +/sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT + +然后保存: + +/etc/rc.d/init.d/iptables save +centos 5.3,5.4以上的版本需要用 +service iptables save + +人人都该学编程? +https://www.jiemodui.com/Item/22041 + +How to Install PHP 7 in CentOS 7 +https://www.tecmint.com/install-php-7-in-centos-7/ + + +### .vimrc + +set nocp +set ts=4 +set sw=4 +set smarttab +set et +set ambiwidth=double +colo torte +set nu + +set encoding=UTF-8 +set langmenu=zh_CN.UTF-8 +language message zh_CN.UTF-8 +set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1 +set fileencoding=utf-8 + + +syntax on +filetype plugin indent on + +### .bashrc + +alias rm='rm -i' +alias cp='cp -i' +alias mv='mv -i' +alias vi='vim' + +# Source global definitions +if [ -f /etc/bashrc ]; then + . /etc/bashrc +fi + +export LC_ALL='zh_CN.utf8' + + + + + +Programmed Lessons in QBasic +http://chortle.ccsu.edu/QBasic/index.html 发现这个 qb 教程还挺好的 + +Shiro的三种授权(十二) +https://www.cnblogs.com/qlqwjy/p/7257616.html +走进Java(五)JSTL和EL表达式 +https://blog.csdn.net/u010096526/article/details/50038365 +JSTL获取Parameter参数 +https://blog.csdn.net/kevinxxw/article/details/50884649 +自定义页面方法:${fns:sayHelloToName('zxw')} +https://blog.csdn.net/zhengxiangwen/article/details/40652019 +JSTL表达式的使用(c:if)以及在JS中使用 +https://blog.csdn.net/weistin/article/details/80027218 + + +eclipse ERMaster插件安装 利用ERMaster快速生成db维护文档 +https://my.oschina.net/dajianguo/blog/1622944 + + +Eclipse启动时禁用不必要的验证。 +https://blog.csdn.net/u012726702/article/details/51758596 + +单词补全 Alt + / + + +window---->preferences---->general----->content types----->Text------>java properties file---->UTF-8---->update ------>ok + +Mybatis整合Spring -- typeAliasesPackage +https://blog.csdn.net/sky786905664/article/details/51801933 + +重启 PHP-FPM + +sudo kill -usr2 $(cat /var/run/php-fpm/php-fpm.pid) +查看 PHP 配置 + +php --ini + +解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题 +https://blog.csdn.net/z69183787/article/details/19911935 + +Tomcat中server.xml配置详解 +https://www.cnblogs.com/yanghua1012/p/5869192.html +tomcat context元素属性介绍 +http://outofmemory.cn/code-snippet/3035/tomcat-context-element-property-introduction +tomcat 8.0特性 +https://blog.csdn.net/hmy1106/article/details/51270761 +Tomcat 7 的七大新特性 +http://www.iteye.com/news/17928/ +Tomcat 5.5.x到Tomcat 6.0(tomcat6新特性及变化) +https://blog.csdn.net/wlanye/article/details/8570891 + +SpringMVC重要注解(四)@ModelAttribute +https://blog.csdn.net/lovesomnus/article/details/78873089 +java怎么用一行代码初始化ArrayList +https://www.itstrike.cn/Question/e74b36fa-c01f-4254-87ec-e549df2abebe.html + +JS 物理引擎 +Physics engine in your JavaScript program +http://slicker.me/javascript/matter.htm +黑苹果 +https://github.com/kholia/OSX-KVM +命令行下的电子表格 +https://github.com/andmarti1424/sc-im +A* +http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html +可视化 +https://qiao.github.io/PathFinding.js/visual/ +游戏算法 +https://www.redblobgames.com/ +4种方法让SpringMVC接收多个对象 +https://blog.csdn.net/lutinghuan/article/details/46820023 +Objects binding in Spring MVC form +https://stackoverflow.com/questions/10138715/objects-binding-in-spring-mvc-form +Spring 4 官方文档学习 Web MVC 框架 +https://www.cnblogs.com/t3306/p/7244134.html +Allow binding=false on @ModelAttribute +https://github.com/spring-projects/spring-framework/commit/2e7470b27f0eaae042334cd86f212cd958676be0 +SpringMVC表单标签和表单标签简介 +https://blog.csdn.net/hp_yangpeng/article/details/51906654 +Myths about /dev/urandom +https://www.2uo.de/myths-about-urandom +With Undefined Behavior, Anything is Possible +https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html +How to Read 100s of Millions of Records per Second from a Single Disk +https://clemenswinter.com/2018/08/13/how-read-100s-of-millions-of-records-per-second-from-a-single-disk/ +Introduction to Go Modules +https://roberto.selbach.ca/intro-to-go-modules/ + +轻量级前端框架 +https://mithril.js.org/ + +Docker的web端管理平台对比(DockerUI 、Shipyard、Portainer、Daocloud) +https://blog.csdn.net/qq273681448/article/details/75007828 +Docker可视化管理工具Portainer的安装配置及使用 +https://blog.csdn.net/bbwangj/article/details/80973219 +Swarm -- 搭建Docker集群 +https://blog.csdn.net/a632189007/article/details/78756339 + +How to use UTF-8 in resource properties with ResourceBundle +https://stackoverflow.com/questions/4659929/how-to-use-utf-8-in-resource-properties-with-resourcebundle +Eclipse 在线安装properties编辑插件 +https://www.cnblogs.com/DylanZ/p/6428709.html + + +稍等两分钟,就会出现插件列表,选择PropertiesEditor,然后Next. + +Where does forever store console.log output? +https://stackoverflow.com/questions/21021186/where-does-forever-store-console-log-output + +forever start -o out.log -e err.log my-script.js + + +Learn You a Haskell for Great Good! +http://learnyouahaskell.com/chapters + +owncloud+collabora 实现网盘在线预览 +https://blog.csdn.net/a295184686/article/details/78632706 + +Epigrams on Programming +http://pu.inf.uni-tuebingen.de/users/klaeren/epigrams.html + + +程序员不能把自己全部的时间都用来换钱,而是要拿出20%~40%的可支配时间来学习、尝试和应用新的技术,让自己的技术栈保持连续更新。 + + +Guacamole 通过浏览器远程访问服务器 +https://www.jianshu.com/p/ebaba8ca17de + +vim buffer 操作 + +:bn -- buffer列表中下一个 buffer   +:bp -- buffer列表中前一个 buffer   +:b# -- 你之前所在的前一个 buffer +:bdelete num -- 删除第num编号buffer + +JavaScript Space After Function [closed] +https://stackoverflow.com/questions/9300636/javascript-space-after-function +基于Docker快速搭建多节点Hadoop集群 +http://dockone.io/article/395 + + +How to Setup Single Node Hadoop Cluster Using Docker +https://linoxide.com/cluster/setup-single-node-hadoop-cluster-docker/ + +A Road to Common Lisp +http://stevelosh.com/blog/2018/08/a-road-to-common-lisp/#get-a-lisp + +C compiler with support for structs written in Assembly +https://news.ycombinator.com/item?id=17851311 + +Oops, I Wrote a C++ Compiler +https://news.ycombinator.com/item?id=17851293 + +Double Buffer +http://gameprogrammingpatterns.com/double-buffer.html + +JSCPP, a simple C++ interpreter written in JavaScript +https://news.ycombinator.com/item?id=17843293 + +Ubuntu 16.04 硬盘安装 +https://www.cnblogs.com/dzlixu/p/5e9475c3990d720ca22e18b730b01d57.html + + +## 编程挑战 + +### 挑战 1: + +输出 1000 以内的斐波那契数列,用空格隔开,每行 5 个显示。 + +### 挑战 2: + +假设公司年会抽奖,一等奖1名,二等奖3名,三等奖5名,共100人抽奖,写一个抽奖程序来抽奖。 +奖品不要多发,不要漏发,考虑通用性和边界处理。以后大家工作后肯定会遇到类似场景的。 + +### 挑战 3: + +写程序解析出一个网址的各个部分 + +比如 https://123.abc.com:8000/hello/world?x=111&y=222&z=333 这个网址要求输出如下 + +协议: https +域名: 123.abc.com +端口: 8000 +路径: /hello/world +参数: x: 111, y: 222, z: 333 + +### 挑战 4 : + +随机生成 100 个 15 以内的随机正整数 + +1、统计出每个数字出现的次数 +2、统计出出现次数最多和出现次数最少的前三个数字 +3、统计出偶数和奇数各自出现的次数和它们的和 +4、统计出出现次数最多的偶数和奇数 + + + + +## docker 常用命令 + + + sudo docker build -t sshd:ubuntu2 . + sudo docker run -d --name ssh_test -p 10122:22 sshd:ubuntu2 + sudo docker run -d --name ssh_test2 -P sshd:ubuntu3 + sudo docker commit 161f67ccad50 sshd:ubuntu + sudo docker exec -it b1141d7b1937 bash + + + sudo /home/haohu/.local/bin/wssh --address='0.0.0.0' --port=80 + ssh root@localhost -p 10122 + + docker run -it sequenceiq/hadoop-docker:2.7.0 /etc/bootstrap.sh -bash + + sudo docker ps -a + docker network create hadoop + sudo docker-compose up -d + sudo docker-compose stop + sudo docker exec -it namenode bash + + hadoop fs -put /etc/issue / + hadoop fs -ls / + + + + git pull tencent master --allow-unrelated-oo + + sudo docker run -d --name novnc -p 6080:80 dorowu/ubuntu-desktop-lxde-vnc + + sudo docker ps --filter ancestor=sshd:ubuntu2 \ + --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}" + + sudo docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name prtainer-test docker.io/portainer/portainer + + docker run -m 512m --memory-swap 1G -it -p 58080:8080 --restart=always + --name bvrfis --volumes-from logdata mytomcat:4.0 /root/run.sh + docker update --restart=always xxx + sudo docker run --restart=on-failure:10 redis + + sudo docker swarm init --advertise-addr 192.168.1.15 + docker swarm join-token worker + docker swarm join --token SWMTKN-1-2nifhbsha6kzfu7pacy93z1niki425t2f3t807y1kzehxhsval-5zns821bezcby2k1o0v7y946z 192.168.1.15:2377 + sudo docker node ls + + sudo docker service create --replicas 1 --name hadoop-test01 sequenceiq/hadoop-docker /etc/bootstrap.sh + sudo docker service ps hadoop-test01 + sudo docker service inspect --pretty hadoop-test01 + docker service scale helloworld=2 + sudo docker service rm hadoop-test01 + + docker swarm leave + + sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ssh.1.uoyp9smajh939e98yc40ygm5z + + + +sudo sed -i "s|EXTRA_ARGS='|EXTRA_ARGS='--registry-mirror=http://1d20ae13.m.daocloud.io |g" /var/lib/boot2docker/profile + +Docker(六):Docker 三剑客之 Docker Swarm +https://baijiahao.baidu.com/s?id=1598047425152066542&wfr=spider&for=pc + +Docker Swarm 部署Mysql/Mariadb高可用主从复制集群 +http://www.chairis.cn/blog/article/90 + +docker 容器日志清理方案 +http://www.chairis.cn/blog/article/94 + +docker应用-5(使用overlay 网络进行容器间跨物理主机通信) +https://www.jianshu.com/p/4bbaf761fad8 + +Docker Swarm集群部署实践 +http://www.chairis.cn/blog/article/89 + + +在使用docker run启动容器时,使用--restart参数来设置: +--restart具体参数值详细信息: +no - 容器退出时,不重启容器; +on-failure - 只有在非0状态退出时才从新启动容器; +always - 无论退出状态是如何,都重启容器; + +Docker容器开机自动启动 +https://blog.csdn.net/menghuanbeike/article/details/79261828 + +使用DockerToolbox自动创建Swarm集群+Portainer图形化管理的脚本 +https://blog.csdn.net/CSDN_duomaomao/article/details/73430919 + +Docker-Hadoop ALL-IN-ONE +https://www.jianshu.com/p/6bcd72083c08 + + + +Docker Container Executor +https://hadoop.apache.org/docs/r2.7.2/hadoop-yarn/hadoop-yarn-site/DockerContainerExecutor.html +Docker image for Hadoop +https://github.com/bigdatafoundation/docker-hadoop + +使用Docker在本地搭建Hadoop分布式集群 +https://www.cnblogs.com/onetwo/p/6419925.html +将Linux系统的home、var目录迁移到新分区 +https://blog.csdn.net/davidhopper/article/details/79768073 +How do I change the Docker image installation directory? +https://forums.docker.com/t/how-do-i-change-the-docker-image-installation-directory/1169 +关于docker的15个小tip +http://www.cnblogs.com/elnino/p/3899136.html +运行 MapReduce 样例 +https://blog.csdn.net/chengqiuming/article/details/78826143 + +Docker环境下Hadoop分布式集群搭建 +https://blog.csdn.net/zhangfan1212/article/details/54791334 + +Docker-Hadoop ALL-IN-ONE +https://www.jianshu.com/p/6bcd72083c08 +Docker创建私有仓库+SSL+AUTH+WEB +https://www.jianshu.com/p/d85398240f05 +Docker-Compose入门 +https://blog.csdn.net/chinrui/article/details/79155688 +使用docker部署hadoop hdfs +http://fatkun.com/2017/12/deploy-hadoop-hdfs-use-docker.html + +Docker Machine 是什么? +https://www.cnblogs.com/sparkdev/p/7044950.html + + + + + +http://npm.taobao.org/mirrors/chromedriver/ +v2.40 Supports Chrome v66-68 +http://npm.taobao.org/mirrors/chromedriver/2.40/notes.txt + +Windows下的Jupyter Notebook 安装与自定义启动(图文详解) +http://www.cnblogs.com/zlslch/p/6984403.html + +pip install selenium + +Python3.5+selenium操作Chrome浏览器 +https://www.cnblogs.com/drake-guo/p/6188366.html + +selenium 安装与 chromedriver安装 +https://www.cnblogs.com/technologylife/p/5829944.html + +最详尽使用指南:超快上手Jupyter Notebook +https://blog.csdn.net/DataCastle/article/details/78890469 + +Python+Selenium WebDriver API:浏览器及元素的常用函数及变量整理总结 +https://www.cnblogs.com/yufeihlf/p/5764807.html + +mkdir ~/.pip/ + +vi ~/.pip/pip.conf + + [global] + index-url = https://pypi.tuna.tsinghua.edu.cn/simple + [install] + trusted-host=mirrors.aliyun.com + +pip install virtualenv +virtualenv venv +echo venv >> .gitignore + +pip install Flask +pip install docker +pip freeze > requirements.txt + +mkdir static +mkdir templates + +. venv/bin/activate +export FLASK_ENV=development +export FLASK_APP=dockerweb.py +flask run --host=0.0.0.0 + +wssh 目录 +/home/haohu/.local/lib/python2.7/site-packages/webssh +wssh debug 模式会去掉 csrf +sudo /home/haohu/.local/bin/wssh --address='0.0.0.0' --port=8000 --debug=true + +screen -dmS dockerweb +screen -r dockerweb + + +8天学会Hadoop +https://ke.qq.com/course/229879 +https://ke.qq.com/course/276816 +https://ke.qq.com/course/287048 + + +GRANT ALL PRIVILEGES ON techaction.* TO 'root'@'%' IDENTIFIED BY 'password'; +GRANT ALL PRIVILEGES ON techaction.* TO 'root'@'localhost' IDENTIFIED BY 'password'; +GRANT SELECT ON *.* TO 'readonly'@'localhost' IDENTIFIED BY 'readonly'; +flush privileges; + +mysql添加用户、修改权限,修改登录权限ip +https://www.cnblogs.com/lemon-flm/p/7597879.html + +An Introduction to Modern CMake +https://cliutils.gitlab.io/modern-cmake/ + +PDF 转换 +https://www.ilovepdf.com/ + +sudo apt-get update +sudo apt-get upgrade + +(实用)Ubuntu 、CentOS更换国内源 +https://www.cnblogs.com/Security-Darren/p/3947952.html + + + +curl -o /dev/null -s -w 'status=%{http_code}\ndns-time=%{time_namelookup}s\nconnec-time=%{time_connect}s\nserver-time=%{time_starttransfer}s\ntota-ltime=%{time_total}\ncontent-length=%{size_download}\n' http://techaction.cn + + + + + + +function reserved_ip($ip) +{ + $reserved_ips = array( // not an exhaustive list + '167772160' => 184549375, /* 10.0.0.0 - 10.255.255.255 */ + '3232235520' => 3232301055, /* 192.168.0.0 - 192.168.255.255 */ + '2130706432' => 2147483647, /* 127.0.0.0 - 127.255.255.255 */ + '2851995648' => 2852061183, /* 169.254.0.0 - 169.254.255.255 */ + '2886729728' => 2887778303, /* 172.16.0.0 - 172.31.255.255 */ + '3758096384' => 4026531839, /* 224.0.0.0 - 239.255.255.255 */ + ); + + $ip_long = sprintf('%u', ip2long($ip)); + + foreach ($reserved_ips as $ip_start => $ip_end) + { + if (($ip_long >= $ip_start) && ($ip_long <= $ip_end)) + { + return TRUE; + } + } + return FALSE; +} + +var_dump(reserved_ip('127.0.0.1')); // reserved (localhost) +var_dump(reserved_ip('74.125.140.101')); // not reserved (Google) + + +iView 发布 3.0 版本,以及开发者社区等 5 款新产品 +https://www.v2ex.com/t/475227#reply10 + +git config --global core.editor vim +git config --global core.autocrlf true +git config --global core.safecrlf true \ No newline at end of file diff --git a/python/django/djangogirls/.gitignore b/python/django/djangogirls/.gitignore new file mode 100644 index 00000000..33469988 --- /dev/null +++ b/python/django/djangogirls/.gitignore @@ -0,0 +1,5 @@ +*.pyc +__pycache__ +myvenv +db.sqlite3 +.DS_Store diff --git a/python/selenium/output.csv b/python/selenium/output.csv new file mode 100644 index 00000000..36cca928 --- /dev/null +++ b/python/selenium/output.csv @@ -0,0 +1,11 @@ +���,��Ʒ,����,��Ǯ,���� +1,Python��� �����ŵ�ʵ��,https://item.jd.com/11993134.html,72.10,12��+ +2,Python�����̳̣���3�棩,https://item.jd.com/12279949.html,80.20,12��+ +3,PYTHON�����̳�(������),https://item.jd.com/24675618191.html,72.00,800+ +4,Effective Python����д������Python�����59����Ч����,https://item.jd.com/11864820.html,46.10,2400+ +5,�����ѧPython��ȫ�ʰ棩,https://item.jd.com/30123476517.html,39.90,20+ +6,Python�߼����,https://item.jd.com/12067698.html,42.30,1000+ +7,Python����ѧϰ����ʵ��,https://item.jd.com/15566703912.html,43.00,40+ +8,Python������� ȫ3�� Python��̴����ŵ�ʵ�� ���������ѧϰ �����̳̳���,https://item.jd.com/20101624082.html,160.90,30+ +9,ȤѧPython���,https://item.jd.com/11410467.html,41.40,1000+ +10,��R������ Python���ݿ�ѧ�ֲ� ���ݷ��������鼮 ����ѧϰ NumPy���ݴ洢 Ma,https://item.jd.com/31244717228.html,77.85,0 diff --git a/python/untitled b/python/untitled new file mode 100644 index 00000000..e69de29b From d5ac656127c499f191dca765c38971ac3bf714e2 Mon Sep 17 00:00:00 2001 From: haohu Date: Fri, 12 Oct 2018 11:03:11 +0800 Subject: [PATCH 012/137] =?UTF-8?q?=E6=B3=9B=E5=9E=8B=E5=8F=8D=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- csharp/test.cs | 24 +++++++++++ mypost/tech_note.md | 101 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 csharp/test.cs diff --git a/csharp/test.cs b/csharp/test.cs new file mode 100644 index 00000000..eea9c5fa --- /dev/null +++ b/csharp/test.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace myApp +{ + class Task { public T MyT {get; set;} } + + class Program { + static void Main(string[] args) { + Task> t = Get>(); + Console.WriteLine(t.MyT.Count); + Console.WriteLine("ok"); + } + + static Task Get() { + var ret = new Task(); + Type itemType = typeof(T).GenericTypeArguments[0]; + var constructedListType = typeof(List<>).MakeGenericType(itemType); + var instance = Activator.CreateInstance(constructedListType); + ret.MyT = (T)instance; + return ret; + } + } +} \ No newline at end of file diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 8fff2a64..e78c8291 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1755,4 +1755,103 @@ https://www.v2ex.com/t/475227#reply10 git config --global core.editor vim git config --global core.autocrlf true -git config --global core.safecrlf true \ No newline at end of file +git config --global core.safecrlf true + + +sudo su +mkdir /data/bakup +mysqldump -uroot -p --all-databases >/data/bakup/dump-`date +%Y%m%d%H%M%S`.sql + +Can I restore a single table from a full mysql mysqldump file? +https://stackoverflow.com/questions/1013852/can-i-restore-a-single-table-from-a-full-mysql-mysqldump-file + +腾讯工程师带你深入解析 MySQL binlog +https://zhuanlan.zhihu.com/p/33504555 + + +ubuntu开启crontab日志记录及解决No MTA installed, discarding output问题 +http://www.pooy.net/ubuntu-open-crontab-logging-and-resolution-no-mta-installed-discarding-output-problem.html + +ERROR: Failed to allocate directory watch: Too many open files +sysctl fs.inotify.max_user_instances=512 + + +解决(CRON) info (No MTA installed, discarding output) +https://blog.csdn.net/win_turn/article/details/53000899 + + +像素 +https://art.pixlab.io/#picLoader + +linux系统挂载U盘,中文文件名乱码解决方案 +https://www.cnblogs.com/zhouqinxiong/p/3497293.html + +Linux下添加新硬盘,分区及挂载 +https://www.cnblogs.com/jiu0821/p/7209825.html + +挂载 U 盘 +mount -o iocharset=utf8 /dev/sdb4 /mnt + +## 分区 + + # 查看分区表,/dev/sda,/dev/sdb 表示硬盘,/dev/sda1, /dev/sda2 表示分区 + fdisk -l |grep '/dev/sd[abcd]' + + # 对 /dev/sdb 分区 + fdisk /dev/sdb + + # 添加新分区 + n + + # e 为扩展分区, p 为主分区 + p + + # 确认分区起始点,如果是新硬盘,输入 1,一般情况下直接回车 + 1 + + #输入分区大小,以下表示 1G,注意 m 是小写 + +1024m + + # 保存分区 + w + + # 查看新分好的区 + fdisk -l + + # 格式化 + mkfs -t ext4 -c /dev/sdb1 + + # 挂载 + mount /dev/sdb1 /www + + # 查看挂载的分区大小 + df -TH + + # 设置为开机自动挂载 + echo '/dev/sdb1 /www ext4 defaults 1 2' >> /etc/fstab + + +导出镜像 +docker save -o vnc.tar vnc:0.01 + +查看磁盘 IO +iostat -d -x -k 1 | grep -E '(Device|sd[abcd])' + + +docker镜像的导入和导出 +https://blog.csdn.net/x6_9x/article/details/72891404 + + +彻底禁用Chrome的“请停用以开发者模式运行的扩展程序”提示 +提示 +https://www.cnblogs.com/liuxianan/p/disable-chrome-extension-warning.html 真够复 真够复杂的 + + + +## 重装系统 + +备份 + +- chrome 收藏夹 +- putty 配置 +- git key \ No newline at end of file From 59720ee8177201df2a3b7b712d5bdf7e923fb8f2 Mon Sep 17 00:00:00 2001 From: haohu Date: Sat, 13 Oct 2018 15:14:20 +0800 Subject: [PATCH 013/137] change --- mypost/tech_note.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mypost/tech_note.md b/mypost/tech_note.md index e78c8291..905cd4f2 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1854,4 +1854,10 @@ https://www.cnblogs.com/liuxianan/p/disable-chrome-extension-warning.html 真够 - chrome 收藏夹 - putty 配置 -- git key \ No newline at end of file +- git key + + +至强CPU多数都是可以配置多路的,就是多个CPU放在同一个板子上,这一点是i7无法做到的。 +至强E5系列里,四位数字的第一位表示的就是CPU的个数,比如Xeon E5-2696 v3,表示此款CPU支持双路, +Xeon E5-4655 v3支持四路,那么我给的列表里E5最高配置是Xeon E5-4669 v3,四路,每路18核,每核双线程, +那么要组装到板子上,在Windows任务管理器里就可以看到壮观的144个核心的场面, \ No newline at end of file From 2eb76096ea5ab98db70f00666f8671f3b550f9ef Mon Sep 17 00:00:00 2001 From: haohu Date: Thu, 21 Feb 2019 09:39:44 +0800 Subject: [PATCH 014/137] add docs --- mypost/programmer_graduation.md | 45 ++++ mypost/tech_note.md | 427 +++++++++++++++++++++++++++++++- txt/nginx_waf.conf | 150 +++++++++++ 3 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 mypost/programmer_graduation.md create mode 100644 txt/nginx_waf.conf diff --git a/mypost/programmer_graduation.md b/mypost/programmer_graduation.md new file mode 100644 index 00000000..62d66be1 --- /dev/null +++ b/mypost/programmer_graduation.md @@ -0,0 +1,45 @@ +# 程序员成人礼 + +## 编程基础 + +- 实现数字 + - 数字的二进制表示及应用,linux 权限表示,modbuf 协议解析,编程珠玑 bitmap,数据内部的 bitmap +- 实现字符串 +- 实现列表 +- 实现字典 +- 日志 + +## 网络基础 + +- tcp echo server +- web server +- 多路复用模型 +- 多线程,同步,锁 +- 无锁队列 +- 内存池,零拷贝实现 +- 多进程,进程通信 +- 信号处理,优雅重启 + +## 编译解析 +- 状态机,url 解析, +- 配置解析,ini,xml,json +- 正则引擎 +- http 协议解析 +- 实现一个模版 +- 实现一个脚本 + +## db + +- db 实现 +- 索引实现 +- 二分查找 +- 树查找 +- SQL 解析 + +## MVC + +- Model +- View +- Controller +- Library +- Helper diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 905cd4f2..4788c5a9 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1860,4 +1860,429 @@ https://www.cnblogs.com/liuxianan/p/disable-chrome-extension-warning.html 真够 至强CPU多数都是可以配置多路的,就是多个CPU放在同一个板子上,这一点是i7无法做到的。 至强E5系列里,四位数字的第一位表示的就是CPU的个数,比如Xeon E5-2696 v3,表示此款CPU支持双路, Xeon E5-4655 v3支持四路,那么我给的列表里E5最高配置是Xeon E5-4669 v3,四路,每路18核,每核双线程, -那么要组装到板子上,在Windows任务管理器里就可以看到壮观的144个核心的场面, \ No newline at end of file +那么要组装到板子上,在Windows任务管理器里就可以看到壮观的144个核心的场面, + +Intel超级大杀器发布:28核56线程桌面CPU,4.3G还不锁频 +http://diy.pconline.com.cn/1180/11808584.html + +ubuntu16.04开机启动字符界面 +https://jingyan.baidu.com/article/948f5924ee2a5dd80ff5f9e4.html + +非常实用的Linux 系统监控工具 +https://www.cnblogs.com/mengdeep/p/5296991.html + +了解VMware预订限制和共享 +Understanding VMware Reservations Limits and Shares +http://www.vfrank.org/2013/09/19/understanding-vmware-reservations-limits-and-shares/ + +Docker: 限制容器可用的内存 +http://www.cnblogs.com/sparkdev/p/8032330.html + +Docker service Limits and Reservations +https://stackoverflow.com/questions/38200028/docker-service-limits-and-reservations + +浮点算法 +echo "scale=2;$a/$b" | bc + + +Swarm mode: Inspect worker node's container on manager node +https://stackoverflow.com/questions/39237998/swarm-mode-inspect-worker-nodes-container-on-manager-node + + +Error loading config file XXX.dockerconfig.json permission denied +https://blog.csdn.net/xukai0110/article/details/80637884 + +sudo chown "$USER":"$USER" /home/"$USER"/.docker -R +sudo chmod g+rwx "/home/$USER/.docker" -R + +How can I use docker without sudo? +https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo + +sudo groupadd docker +sudo gpasswd -a $USER docker +newgrp docker +docker ps + +How to determine where an environment variable came from +https://unix.stackexchange.com/questions/813/how-to-determine-where-an-environment-variable-came-from + + +ubuntu 死机,查看日志 + + +Linux Kernel panic issue: How to fix hung_task_timeout_secs and blocked for more than 120 seconds problem +https://www.blackmoreops.com/2014/09/22/linux-kernel-panic-issue-fix-hung_task_timeout_secs-blocked-120-seconds-problem/ + + +NO-BREAK SPACE +https://stackoverflow.com/questions/5237989/nonbreaking-space + + +VNC Tight Encoder - Comparison Results +http://tightvnc.com/archive/compare.html + +Comparing 7 Monitoring Options for Docker +https://rancher.com/comparing-monitoring-options-for-docker-deployments/ + + +一种基于负载预测的DockerSwarm集群资源调度优化方法与流程 +http://www.xjishu.com/zhuanli/55/201710461892.html + +运维之我的docker-集群的管理-swarm +http://blog.51cto.com/nginxs/1894432 + +Docker Stack Swarm - Service Replicas are not spread for Mutli Service Stack +https://stackoverflow.com/questions/44868123/docker-stack-swarm-service-replicas-are-not-spread-for-mutli-service-stack + + +https://www.cnblogs.com/aguncn/p/6904522.html + +清理Docker占用的磁盘空间 +https://blog.csdn.net/weixin_32820767/article/details/81196250 + +Docker网络和 overlay跨主机通讯 +http://blog.51cto.com/littledevil/1922922 + +Docker Swarm 入门:Service Network 管理 +https://www.jianshu.com/p/60bccbdb6af9 + + +Battlefield: Calico, Flannel, Weave and Docker Overlay Network +http://xelatex.github.io/2015/11/15/Battlefield-Calico-Flannel-Weave-and-Docker-Overlay-Network/ + + +Demystifying Docker overlay networking +http://blog.nigelpoulton.com/demystifying-docker-overlay-networking/ + +DOCKER_OPTS do not work in config file /etc/default/docker +https://stackoverflow.com/questions/27763340/docker-opts-do-not-work-in-config-file-etc-default-docker + +journalctl -xe + +金步国作品集 各种手册翻译 +http://www.jinbuguo.com/ + +分页无截断查看日志 +journalctl -rxn -u docker.service | less +查看错误日志 +journalctl -p err..alert +查看内核日志 +journalctl -k +显示本次启动后的所有日志: +journalctl -b + +journalctl: how to prevent text from truncating in terminal +https://unix.stackexchange.com/questions/229188/journalctl-how-to-prevent-text-from-truncating-in-terminal + +systemd 之 journalctl +https://www.cnblogs.com/itxdm/p/Systemd_log_system_journalctl.html + +Systemd 常规操作与彩蛋 +http://www.cnblogs.com/itxdm/p/Systemd_regular_operation_with_eggs.html + + +DOCKER图形页面管理工具--3种,shipyard最强大,其次是portainer +https://blog.csdn.net/xl_lx/article/details/81183956 + +Protect the Docker daemon socket +docker 进程认证 +https://docs.docker.com/engine/security/https/ + +Docker security 安全 +https://docs.docker.com/engine/security/security/#kernel-namespaces + + + 算机专业课一体化支撑平台 北航 CG OJ +http://www.cjudge.net/FAQs.html + +linux开发神器--Tmux +https://www.cnblogs.com/ArsenalfanInECNU/p/5756763.html + + +tmux new -s monitor + +按下 Ctrl-b 后的快捷键如下: + + +% 创建一个水平窗格 +" 创建一个竖直窗格 +方向键切换窗格 +q 显示窗格的编号 +o 在窗格间切换 +ctrl + 方向键, alt + 方向键,切换大小 +b 离开会话,然后可以用 tmux attach 挂载 +x 关闭当前窗格 + +node js 进程守护神forever +https://blog.csdn.net/jbboy/article/details/35281225 + +apt-cache show xserver-xorg | grep Version +https://superuser.com/questions/366505/how-to-find-out-xorg-version-or-whats-my-xorg-version + +htop 快捷键,H 切换用户线程 K 切换内核线程 + +Why docker uses port numbers from 32768-65535? +https://stackoverflow.com/questions/40787524/why-docker-uses-port-numbers-from-32768-65535 + +容器删除后,主机映射给容器的端口为何并立即未回收利用? +https://segmentfault.com/q/1010000000496866 + +NGINX as a WebSocket Proxy +https://www.nginx.com/blog/websocket-nginx/ + + +Ganglia 权威指南-安装Ganglia过程 +http://www.cnblogs.com/chris-cp/p/4324392.html + + + +第2章 rsync(一):基本命令和用法 +https://www.cnblogs.com/f-ck-need-u/p/7220009.html + +rsync -avP /var/www/html/ action@192.168.1.102:/var/www/html + + +Eclipse快捷键 10个最有用的快捷键 +https://blog.csdn.net/lynn349x/article/details/56282704 + +How to convert an rtf string to text in C# +https://stackoverflow.com/questions/5634525/how-to-convert-an-rtf-string-to-text-in-c-sharp + +grep 和 awk的buffer +https://blog.csdn.net/csCrazybing/article/details/78096301 + +How to fix stdio buffering +http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html + + +Bash History Display Date And Time For Each Command +https://www.cyberciti.biz/faq/unix-linux-bash-history-display-date-time/ + + +hadoop 集群 硬件配置 +http://info.ipieuvre.com/article/201506/250.html +https://www.oschina.net/translate/how-to-select-the-right-hardware-for-your-new-hadoop-cluster + +Docker 日志都在哪里?怎么收集? +https://www.cnblogs.com/YatHo/p/7866029.html + +PHP file_get_contents fails to read zip on windows +https://stackoverflow.com/questions/27405430/php-file-get-contents-fails-to-read-zip-on-windows + +baidu ai studio +http://aistudio.baidu.com/#/projectDetail/35620 + +百度实验平台 +http://abcinstitute.baidu.com/lab/experiment/list;JSESSIONID=d8d1ce38-2afe-4324-b976-2f908f9dea15 + +机器学习——15分钟透彻理解感知机 +https://blog.csdn.net/yxhlfx/article/details/79093456 + +自然语言处理 中文分词 词性标注 命名实体识别 依存句法分析 新词发现 关键词短语提取 自动摘要 文本分类聚类 拼音简繁 +https://github.com/hankcs/HanLP#1-%E7%AC%AC%E4%B8%80%E4%B8%AAdemo + +搜狗实验室 +http://www.sogou.com/labs/ + +word2vec原理推导与代码分析 +http://www.hankcs.com/nlp/word2vec.html + +识别数字 +http://www.paddlepaddle.org/documentation/docs/zh/develop/beginners_guide/quick_start/recognize_digits/README.cn.html + +PaddlePaddle 新手入门 +http://www.paddlepaddle.org/documentation/docs/zh/1.2/beginners_guide/index.html + +Deep Learning(深度学习) 中文版 +https://github.com/exacity/deeplearningbook-chinese + +神经网络的四块积木:全连接,激活函数,卷积,池化 +三种神经网络模型:Softmax回归模型,多层感知机模型,卷积神经网络模型 +LeNet-5 + +三分钟带你对 Softmax 划重点 +https://blog.csdn.net/red_stone1/article/details/80687921 +小白都能看懂的softmax详解 +https://blog.csdn.net/bitcarmanlee/article/details/82320853 + +Istio是啥?一文带你彻底了解! +https://www.sohu.com/a/270131876_463994 + +nginx配合modsecurity实现WAF功能 +https://www.52os.net/articles/nginx-use-modsecurity-module-as-waf.html +https://www.techrepublic.com/article/how-to-install-and-enable-modsecurity-with-nginx-on-ubuntu-server/ +CentOS Linux下用Nginx和Naxsi搭建Web应用防火墙 +http://blog.cnwyhx.com/centos-nginx-naxsi-install/ +nginx下安装配置naxsi waf防火墙(附完整编译、配置) +http://f2ex.cn/nginx-installed-configuration-naxsi-waf/ + +nginx的sql注入过滤配置 +http://blog.itpub.net/30208512/viewspace-1578399/ + +Nginx 配置格式化小工具 +https://github.com/fangpeishi/ngxfmt + +轻松玩转OpenWAF之安装篇.md +https://github.com/titansec/OpenWAF/blob/master/doc/%E8%BD%BB%E6%9D%BE%E7%8E%A9%E8%BD%ACOpenWAF%E4%B9%8B%E5%AE%89%E8%A3%85%E7%AF%87.md + +How to install and enable ModSecurity with NGINX on Ubuntu Server +https://www.techrepublic.com/article/how-to-install-and-enable-modsecurity-with-nginx-on-ubuntu-server/ + + +Compatibility of ModSecurity with NginX waf +https://stackoverflow.com/questions/44978041/compatibility-of-modsecurity-with-nginx + + +2018-12-27T16:45:54+08:00 404 0.000 - 119.180.52.108 "POST /admin/login HTTP/1.1" 571 "http://techaction.cn/admin/login" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" "-" + + +2018/12/27 16:46:52 [error] 9952#0: *132 open() "/data/release/ytaction/src/public/admin/login" failed (2: No such file or directory), client: 119.180.52.108, server: techaction.cn, request: "POST /admin/login HTTP/1.1", host: "techaction.cn", referrer: "http://techaction.cn/admin/login" + + +How to Install Nginx with libModSecurity and OWASP core rule set on Ubuntu 16 +https://www.hugeserver.com/kb/install-nginx-libmodsecurity-owasp-core-ruleset-ubuntu16/ + +Ubuntu 16.04上使用libmodsecurity的Nginx和OWASP ModSecurity核心规则 +https://www.howtoing.com/nginx-with-libmodsecurity-and-owasp-modsecurity-core-rule-set-on-ubuntu-1604/ + +How to Install libmodsecurity + nginx on Ubuntu 14.04 +https://help.dreamhost.com/hc/en-us/articles/223608748-How-to-Install-libmodsecurity-Nginx-on-Ubuntu-14-04 + + +斯坦福大学公开课 :机器学习课程 +http://open.163.com/special/opencourse/machinelearning.html +百度 AI 课程 +https://ai.baidu.com/paddlepaddle/player?id=13 + + + +Cleaning up docker to reclaim disk space +https://lebkowski.name/docker-volumes/ + +#!/bin/bash + + # remove exited containers: + docker ps --filter status=dead --filter status=exited -aq | xargs -r docker rm -v + + # remove unused images: + docker images --no-trunc | grep '' | awk '{ print $3 }' | xargs -r docker rmi + + # remove unused volumes (needs to be ran as root): + find '/var/lib/docker/volumes/' -mindepth 1 -maxdepth 1 -type d | grep -vFf <( + docker ps -aq | xargs docker inspect | jq -r '.[]|.Mounts|.[]|.Name|select(.)' + ) | xargs -r rm -fr + + + +CREATE DATABASE IF NOT EXISTS techaction default charset utf8 COLLATE utf8_general_ci; + +Windows远程数据同步工具cwRsync +https://www.cnblogs.com/l1pe1/p/4901031.html + +Netplan – How To Configure Static IP Address in Ubuntu 18.04 using Netplan +https://www.itzgeek.com/how-tos/linux/ubuntu-how-tos/netplan-how-to-configure-static-ip-address-in-ubuntu-18-04-using-netplan.html + +apt 下载 +sudo apt-get install -d network-manager +apt-offline - offline apt package manager +apt-zip - Update a non-networked computer using apt and removable media +apt download + +https://stackoverflow.com/questions/4419268/how-do-i-download-a-package-from-apt-get-without-installing-it + +https://blog.csdn.net/thinktalk/article/details/83716617 + +Ubuntu中启用关闭Network-manager网络设置问题 +https://blog.csdn.net/weixin_42155195/article/details/80683166 + +如何在 Linux 上使用网络配置工具 Netplan +https://linux.cn/article-10095-1.html?pr + +ubuntu使用apt-get制作OfflinePackage +https://blog.csdn.net/ouyangziling/article/details/79056161 + + +缺少rsync也可以通过校验和比较文件。 + +--size-only这意味着rsync将跳过大小匹配的文件,即使时间戳不同。这意味着它将同步比默认行为更少的文件 + +--ignore-times 这意味着rsync将检查每个文件的和,即使时间戳和文件大小匹配。这意味着它将同步比默认行为更多的文件。 + + +rsync “Operation not permitted” +https://serverfault.com/questions/296587/rsync-operation-not-permitted + +函数响应式编程(FRP)思想 +https://blog.csdn.net/fly1183989782/article/details/62053973 + +漫谈FRP之 其实你早就学过FRP +http://insights.thoughtworkers.org/frp-series-0-not-new-concept/ + +函数编程中functor和monad的形象解释 +https://www.jdon.com/idea/functor-monad.html + +bootstrap dropdown hover menu +https://codepen.io/bsngr/pen/frDqh + +How to create a collapsing tree table in html/css/js? 树形结构 +https://stackoverflow.com/questions/5636375/how-to-create-a-collapsing-tree-table-in-html-css-js + +chrome插件 —— 帮助高度还原设计稿 +https://www.cnblogs.com/joyho/articles/5622760.html + +OpenPAI:大规模人工智能集群管理平台 +https://www.msra.cn/zh-cn/news/features/openpai + + +Cython入门到放弃(一) +https://blog.csdn.net/qtlyx/article/details/80614608 + +Best approach to encrypt big files with php +https://stackoverflow.com/questions/16175154/best-approach-to-encrypt-big-files-with-php + +Whole File Encryption/Decryption With PHP +http://monkeylogic.com/whole-file-encryptiondecryption-with-php/ + +All the crypto code you’ve ever written is probably broken +https://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken + +openssl -aes-128-ecb encryption doesn't match python Crypto.Cipher AES encryption +https://stackoverflow.com/questions/48410452/openssl-aes-128-ecb-encryption-doesnt-match-python-crypto-cipher-aes-encryptio + +A Python-to-PHP compatible AES encryption with openssl_encrypt AES-CBC +https://stackoverflow.com/questions/40820661/a-python-to-php-compatible-aes-encryption-with-openssl-encrypt-aes-cbc + +PHP 加密,Python 解密 +A Python-to-PHP compatible AES encryption with openssl_encrypt AES-CBC +https://stackoverflow.com/questions/40820661/a-python-to-php-compatible-aes-encryption-with-openssl-encrypt-aes-cbc + +云脑科技徐昊:AutoML 工程实践与大规模行业应用 +http://www.lyweixiao.com/5314124/20190105A0YCBV00.html + +https://serverfault.com/questions/870568/fatal-error-cant-open-and-lock-privilege-tables-table-storage-engine-for-use +Fatal error: Can't open and lock privilege tables: Table storage engine for 'user' doesn't have this option + +Docker CPU 资源限制——CPU固定核功能测试 +https://www.cnblogs.com/zhenyuyaodidiao/p/5061884.html + +Linux下限制进程的CPU利用率 +https://blog.csdn.net/bailyzheng/article/details/51355384 + +Linux---使用 nice、cpulimit 和 cgroups 限制 cpu 占用率 +https://blog.csdn.net/loyachen/article/details/52167124 + +How do I install Ubuntu Server (step-by-step)? +https://askubuntu.com/questions/340965/how-do-i-install-ubuntu-server-step-by-step + +H5 游戏引擎 + phaser,pixi,enchant.js + +【blockly教程】第一章 Google Blockly教学应用手册 +https://www.cnblogs.com/scratch8/archive/2018/09/12/9637214.html +https://www.cnblogs.com/scratch8/category/1299483.html +https://v.youku.com/v_show/id_XMTg3MTYzNjYzNg==.html +https://blog.csdn.net/oalevel/article/details/81834837 + + +用友T3 +http://www.3322.cc/soft/43731.html \ No newline at end of file diff --git a/txt/nginx_waf.conf b/txt/nginx_waf.conf new file mode 100644 index 00000000..b6af8195 --- /dev/null +++ b/txt/nginx_waf.conf @@ -0,0 +1,150 @@ +location ~* union.*select.*\( { deny all; } +location ~* union.*all.*select.* { deny all; } +location ~* concat.*\( { deny all; } +## Block common exploits +location ~* (<|%3C).*script.*(>|%3E) { deny all; } +location ~* base64_(en|de)code\(.*\) { deny all; } +location ~* (%24&x) { deny all; } +location ~* (%0|%A|%B|%C|%D|%E|%F|127\.0) { deny all; } +location ~* \.\.\/ { deny all; } +location ~* ~$ { deny all; } +location ~* proc/self/environ { deny all; } +location ~* /\.(htaccess|htpasswd|svn) { deny all; } +## Block file injections +location ~* [a-zA-Z0-9_]=(\.\.//?)+ { deny all; } +location ~* [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ { deny all; } +## wordpress security +location ~* wp-config.php { deny all; } +location ~* wp-admin/includes { deny all; } +location ~* wp-app\.log { deny all; } +location ~* (licence|readme|license)\.(html|txt) { deny all; } + + set $block_common_status 0; + if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { + set $block_common_status 1; + } + if ($query_string ~ "base64_(en|de)code\(.*\)") { + set $block_common_status 1; + } + if ($block_common_status = 1) { + return 403; + } + + + + if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) ) + { + # return 404; + return 403; + } + + ## Block SQL injections + set $block_sql_injections 0; + if ($query_string ~ "union.*select.*\(") { + set $block_sql_injections 1; + } + if ($query_string ~ "union.*all.*select.*") { + set $block_sql_injections 1; + } + if ($query_string ~ "concat.*\(") { + set $block_sql_injections 1; + } + if ($block_sql_injections = 1) { + return 403; + } + + ## Block file injections + set $block_file_injections 0; + if ($query_string ~ "[a-zA-Z0-9_]=http://") { + set $block_file_injections 1; + } + if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") { + set $block_file_injections 1; + } + if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") { + set $block_file_injections 1; + } + if ($block_file_injections = 1) { + return 403; + } + + ## Block common exploits + set $block_common_exploits 0; + if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { + set $block_common_exploits 1; + } + if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") { + set $block_common_exploits 1; + } + if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") { + set $block_common_exploits 1; + } + if ($query_string ~ "proc/self/environ") { + set $block_common_exploits 1; + } + if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") { + set $block_common_exploits 1; + } + if ($query_string ~ "base64_(en|de)code\(.*\)") { + set $block_common_exploits 1; + } + if ($block_common_exploits = 1) { + return 403; + } + + ## Block spam + set $block_spam 0; + if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b") { + set $block_spam 1; + } + if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b") { + set $block_spam 1; + } + if ($query_string ~ "\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b") { + set $block_spam 1; + } + if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b") { + set $block_spam 1; + } + if ($block_spam = 1) { + return 403; + } + + ## Block user agents + set $block_user_agents 0; + + + # Disable Akeeba Remote Control 2.5 and earlier + if ($http_user_agent ~ "Indy Library") { + set $block_user_agents 1; + } + + # Common bandwidth hoggers and hacking tools. + if ($http_user_agent ~ "libwww-perl") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "GetRight") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "GetWeb!") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "Go!Zilla") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "Download Demon") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "Go-Ahead-Got-It") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "TurnitinBot") { + set $block_user_agents 1; + } + if ($http_user_agent ~ "GrabNet") { + set $block_user_agents 1; + } + + if ($block_user_agents = 1) { + return 403; + } \ No newline at end of file From a135ac869830c4716cab9df9e28930d3f81a6bfd Mon Sep 17 00:00:00 2001 From: onlytiancai Date: Tue, 5 Mar 2019 11:27:28 +0800 Subject: [PATCH 015/137] x --- python/parse_rss.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 python/parse_rss.py diff --git a/python/parse_rss.py b/python/parse_rss.py new file mode 100644 index 00000000..fbb915ac --- /dev/null +++ b/python/parse_rss.py @@ -0,0 +1,25 @@ +#-*- coding: utf-8 -*- +import time +import feedparser +from googletrans import Translator + +translator = Translator(service_urls=['translate.google.cn']) + +url = 'http://feeds.newscientist.com/tech/' +resp = feedparser.parse(url) +for feed in resp['entries'][:10]: + + title = feed['title'] + cn_title= translator.translate(title, src='http://github.com/en', dest='zh-cn').text + + summary = feed['summary'] + cn_summary = translator.translate(summary, src='http://github.com/en', dest='zh-cn').text + + print(time.strftime('%Y-%m-%d %H:%M:%S',feed['published_parsed'])) + print(title) + print(cn_title) + print(summary) + print(cn_summary) + print('') + + From da14369f67ab1dad8cb3eaff8a714c8df5686059 Mon Sep 17 00:00:00 2001 From: haohu Date: Thu, 7 Mar 2019 15:09:33 +0800 Subject: [PATCH 016/137] tf hello world --- tf/1/1.md | 274 +++++++++++++++++++++++++++++++++++++++++++ tf/1/output_10_0.png | Bin 0 -> 121010 bytes tf/1/output_38_0.png | Bin 0 -> 133778 bytes 3 files changed, 274 insertions(+) create mode 100644 tf/1/1.md create mode 100644 tf/1/output_10_0.png create mode 100644 tf/1/output_38_0.png diff --git a/tf/1/1.md b/tf/1/1.md new file mode 100644 index 00000000..f9c4cfee --- /dev/null +++ b/tf/1/1.md @@ -0,0 +1,274 @@ + +# TensorFlow Hello World 平面你和 + +http://www.tensorfly.cn/tfdoc/get_started/introduction.html + +### 使用 NumPy 生成假数据(phony data), 总共 100 个点. + +`x_data` 是二维数组,每个维度各 100 个点,定义了一个平面 + + +```python +import tensorflow as tf +import numpy as np + +x_data = np.float32(np.random.rand(2, 100)) # 随机输入 +x_data[0][:10] +``` + + + + + array([0.35073978, 0.16348423, 0.7059651 , 0.7696817 , 0.4036316 , + 0.52306384, 0.8748454 , 0.52280265, 0.9512267 , 0.10213694], + dtype=float32) + + + + +```python +x_data[1][:10] +``` + + + + + array([0.33513898, 0.07861521, 0.58426493, 0.87010854, 0.24188931, + 0.64622885, 0.39593607, 0.4805421 , 0.6906034 , 0.41190282], + dtype=float32) + + + +`y_data` 由 `x_data` 经过变换得到,`np.dot` 实现矩阵乘法,要求第一个矩阵的列数和第二个矩阵的行数相同,最后加一个偏移量 + +比如 `y_data[0]` 就等于 `x_data[0][0]*0.1 + x_data[1][0]*0.2 +0.3` + +这里整体的效果,相当于对原始的平面在三维空间进行了一个倾斜旋转,倾斜的参数由一个权重 `W=[0.1, 0.2]` 和偏移量 `b=0.3` 来确定 + + +```python +y_data = np.dot([0.100, 0.200], x_data) + 0.300 +y_data[:10] +``` + + + + + array([0.40210177, 0.33207147, 0.4874495 , 0.55098988, 0.38874102, + 0.48155215, 0.46667175, 0.44838868, 0.53324335, 0.39259426]) + + + +### 原始数据可视化 + +使用 matplotlib 的 `scatter` 功能实现 3D 散点图,x 轴是 `x_data[0]`, y 轴是 `x_data[1]`,z 轴是 `y_data` + + +```python +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +x, y, z = x_data[0], x_data[1], y_data +fig = plt.figure(figsize=(20, 14)) +ax = fig.add_subplot(111, projection='3d') +ax.scatter(x, y, z, c='y') +plt.show() +``` + + +![png](output_10_0.png) + + +### 构造一个线性模型 + +线性模型一般由权重 `W` 和偏移量 `b` 来描述,平面上直线拟合 `W` 是一个标量数字,而本例在三维空间进行平面拟合,所以 `W` 是一个有两个分量的向量。 + + +```python +b = tf.Variable(tf.zeros([1])) +b +``` + + WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. + Instructions for updating: + Colocations handled automatically by placer. + + + + + + + + + + +```python +W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0)) +W +``` + + + + + + + + +y 是模拟的结果,`tf.matmul` 将矩阵 `A` 乘以矩阵 `B`,生成 `A * B`,最后加上偏移量 `b` + + +```python +y = tf.matmul(W, x_data) + b +y +``` + + + + + + + + +### 最小化方差 + +定义损失函数,线性回归里常用的是均方误差,就是真实值和预测值的差的平方和 + + +```python +loss = tf.reduce_mean(tf.square(y - y_data)) +``` + +定义优化器,这里使用梯度下降算法 + + +```python +optimizer = tf.train.GradientDescentOptimizer(0.5) +``` + +使用指定的优化器和损失函数定义一个训练 + + +```python +train = optimizer.minimize(loss) +``` + +### 初始化变量 + + +```python +init = tf.global_variables_initializer() +``` + +### 启动图 (graph) + + +```python +sess = tf.Session() +sess.run(init) +``` + +### 拟合平面 + +我们知道真实的 `W` 为 `[0.1, 0.2]`,`b` 为 `0.3`,看下迭代训练 200 次的拟合效果怎么样 + + +```python +for step in range(0, 201): + sess.run(train) + if step % 20 == 0: + print(step, sess.run(W), sess.run(b)) +``` + + 0 [[ 0.8425213 -0.12354811]] [0.13099673] + 20 [[0.289453 0.12614608]] [0.2357107] + 40 [[0.15044135 0.18556874]] [0.28013656] + 60 [[0.11361164 0.19769716]] [0.29380444] + 80 [[0.10372839 0.1998468 ]] [0.29805225] + 100 [[0.10103785 0.20009856]] [0.2993837] + 120 [[0.1002938 0.20006898]] [0.29980397] + 140 [[0.1000846 0.20003161]] [0.2999374] + 160 [[0.10002476 0.20001256]] [0.29997995] + 180 [[0.10000735 0.20000464]] [0.29999357] + 200 [[0.10000221 0.20000164]] [0.29999793] + + +这里迭代 200 次的结果 `W` 为 `[0.09, 0.19]`, `b` 为 `0.30`,可以看出跟真实值差别非常小了 + +### 拟合效果可视化 + +https://stackoverflow.com/questions/20699821/find-and-draw-regression-plane-to-a-set-of-points + +把原始的分布在三维空间的点,组成一个个的三元组,分别表示 x, y, z 的坐标值 + + +```python +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +points = list(zip(x_data[0],x_data[1],y_data)) +points[:10] +``` + + + + + [(0.35073978, 0.33513898, 0.40210177302360534), + (0.16348423, 0.07861521, 0.33207146525382997), + (0.7059651, 0.58426493, 0.4874494969844818), + (0.7696817, 0.87010854, 0.5509898781776428), + (0.4036316, 0.24188931, 0.3887410223484039), + (0.52306384, 0.64622885, 0.4815521538257599), + (0.8748454, 0.39593607, 0.4666717529296875), + (0.52280265, 0.4805421, 0.44838868379592894), + (0.9512267, 0.6906034, 0.5332433462142945), + (0.10213694, 0.41190282, 0.3925942569971085)] + + + + +```python +w_val = sess.run(W) +b_val = sess.run(b) +``` + + +```python +def cross(a, b): + return [a[1]*b[2] - a[2]*b[1], + a[2]*b[0] - a[0]*b[2], + a[0]*b[1] - a[1]*b[0]] + +def show(points, a, b, c): + # 定义画布 + fig = plt.figure(figsize=(20, 14)) + ax = fig.add_subplot(111, projection='3d') + + # 绘制原始的散点 + xs, ys, zs = zip(*points) + ax.scatter(xs, ys, zs) + + # 绘制拟合平面 + point = np.array([0.0, 0.0, c]) + normal = np.array(cross([1,0,a], [0,1,b])) + d = -point.dot(normal) + xx, yy = np.meshgrid([0,1], [0,1]) + z = (-normal[0] * xx - normal[1] * yy - d) * 1. / normal[2] + ax.plot_surface(xx, yy, z, alpha=0.2, color=[0,1,0]) + + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + + plt.show() + +``` + + +```python +show(points, w_val[0][0],w_val[0][1],b_val[0]) +``` + + +![png](output_38_0.png) + diff --git a/tf/1/output_10_0.png b/tf/1/output_10_0.png new file mode 100644 index 0000000000000000000000000000000000000000..e330f422430cb0230255611c7c02391fa3b0e346 GIT binary patch literal 121010 zcmeGEcRZH={{{|U_9k0q*&(AY*`)036+&jAGD_Kd%P2x5WUq*__pT&6id;rTWM%LB zd8+qke81oO{^S1d_jvp~&Ud|wuIqKa&gVFu$8o%_2yIPeGGazz6bePAs-mEaLg6}~ zP&fpH`0zKDo@J!)8}7YJs``ZR5|6)AUsU>KZPZnM)%*;9 z(Q;ek)+?`38`^iZKP)$|UmNaeEK~gyf}s#L`Y9))L1!#r=(DhObijq#l2`cqwHiO; zi~Y|Z)=7gH+5dcH23u%f{`>b&PLMi;|NWv0lqaM7^QCi1IREtDA6FyXNNt+^^7Wrr z3gUXcNniXQ-^LgH=UYTA8KHk)jQ2Fd(22jV#)bR;|9k(xFaCc%lK*FIOTLR77CU?9 zn>A^Qn3;^Pj+YZ{c_Xn63{H2SvoQ|kRDHyGW+_hujfAMqydufk7vB8+&HIy>t*Kf;=B|jS zkRm4qe-1upBpmIeMUZf<%`nG`CN%h1sJ%!#8J;f3I*B60PIbm3$68{eMW`#1n5^ zZYvkqZe-i>((!1+??mqAXV;BZ_^oT5x-a~M?a)*>p|f;v+um|h?8tRLmgd*9-u2kZ z`$(c?j+P}0Q&RSOOy_6R@U1d)sRl0ZY181L0#|a!7SpypE(mkOkEsbF4<5Zaz|hC+ zw!dSZ9pK_lOv}VT4MD<%gJc=wwY1q6{dO7JyeO5M7w?@sxFCGPAIWyKB$DZN#o=j! znSnpjk9N{me`@)876*l`VKa|aYOQ04!o=8{-+A^aB0o)h{L=!=6n6f0e)N*6^@5qp z(WiDX)g|4u`N*c?MF%7oRb<6bZgh=hMXXN|lAuikWFM%f)EDJdyl8e-lAwYT5zUiPU+*Ssaxx4dnk z4ojK&^G26av3h2@V#^vDqHAg-(2QgZp;%om8TO3tzn@dfp@Lss&E)!*ZA~}ounnqr zmDJO%j_=#J6?v?>XG{D$U6_kYORc@Un1mX)*(bb~WyE8srAwCgUI~!AG$aYN%rGo! zL2J0Dg;|v`1dHY95_MQnmJ3j9?BsSBW5u{@d~SXU3ok!jPmZoGc8ZqY?%*R^iF{m2 zPSs1Y>B_R{T^~NEfAgpsyTcF~U|1B=&>-_>><-GmWZ^M!s<&+Kt0|5Hw6Lss0LLLx z9Jx*+k4!CL2CFAOag+|L=i^6m$BFurxafv7jB&4G9Oqmux!H(_m>7MOR~o?}y5FIq z&kp^nq9Q~y4@Xf)n=gT#Ry-w_%$8|zt{#>5??}nyv_@)tf$#_aAH~jGrtR z-pAu`enNBn#QGI%gJ;Aq7i3UQy#RJNj_ zfhAbt=(o@jRY7zA{pW)|vI9;y*7)K`c&Qz~)ZAL~pslBUu6fAg!Nf;M^D)!7- z_BCYZ`c*hjXZ);)99%U>FR^;m#DA7kHLzr%?>@`Dd-qzZXFcSPo+)JoynK1qRg#4_ zlFHuPlrJH}-X!YUBLSpj6#jH)=sI5K=i@{U^c>4;KV4tnAQjIBGF;P-6 z)ay;3F_^9|Ul?zXDG4S$L<~PKQ7Swe)nWbuE_0el_s*Nlao+NqUn%IQXBUMAAKn?Fh$@_w{dI0hy8tT;iX!~ zzUJ+fl@k>vWQn%&2E!^F8U^~71e5B1uf2VL%g5Z`dhCKg_x$H(apuWa$i4MjAmA^v zFaUG@=om{hfRSp*`w!tvGVVa!xAF1<*Ctp?XW|OFzJwkD5Q4pn&)Pd zsxHn<^~P)L;njI2MID9-xU1*Kh}29a6-(o#VcU-%KQ7`hp1wsZYWz9TGM2iTAQ$e^0tdDj}k0N(=Jl>X*wh6y#YKpC> z5FM?IHWj4*6Y7Q1i&VR20eq%D9JyL9O+4(G#ZxD7C@B?#`H>27;lhQLm8rHDa!dI* zjPh}uoB1yq&qM}BL=dTKYASV}?IzCEy5r>3CQMpg^6=7&6UMTpBlkC(h$Q%%DW_J$ z*nGpss+_U_4Ok${e$R^F2i%M;8XiTRP*$s8oRo!;(pZ5EN_-i!xotl2to8z}cQ5Q2 z(Hd<-L;Au$_GBfWlasdjTQcPTE0k~at&};hOnaE0_?SA}yzPz`vkja2#1+|*&T(TI zyHJlQHaB?gXT~Q(e9HfS*WeiS=X^=seds&CFtg4Ko)g6SUmeBH31ZtJfagyaL+1(( zaJn<}xWrTS5eEIziN`hml9`F|GSXno8RH7;ml0n$lt#D$D%pouw89%Hg6 zoX4yDU~A~YXrnz-E66J6%Z!VTWtCA=xHl}Yk^fHj{t20d2HgUE?E-!FnaVo^2Tl*% z+$Yhiifhy6XB>ZvZ#n~JMFl8xd47H z_iJ6`Fy+cu^*j#PNkzsE00mHO;rj651MW=YGOW+!CEe!cW}jmf7OJ*Hna}z6jyK=t zlupRw=?Zc41R6&RZ)PO=Gv!q#PM)WvBx^UoW_*!;c-Ec>3gAY*r6L&%k1Ca`Sy;)! z(`U~}I%RrEb}@jkZr{1{V({lhohK@5)4Bz?8iQ8jffLiDvLA0*jNig5JcfG0^-wDG zxVAndhllT;3-?#5{_y71$bIOCugc0ENBS#G4)N4RzA?YuT^@C}1}^54Zp=JJvfh4v zY`1RRdUc1X##C^izFMEJV{?afeJD>JiQKMlD^Ce;S0j~#C29Y>9hA05^7!ahML~uE z#y&!0VlnpsF%#p@PObU|9pAoj0`TCNNwmcq%&V-bf@ftqqWD5PTz_(|R(AbROqI+* z^WJ0H{MO^jJ8+L%LqS1?j3c8a{0#w8E*Vcfo5JeQ8bsTrgccMOSbW&>ag}_Yft_bw z+J@FZ&3$fmXCU(*EX2S|AcKJZm+K_#?2>1EZVo{*`D68DklrQwU2S{THPZ&l5}-!7 zUcY{Q+}?0zZoH0o{+tDH;M;QMWJv?TB*?Fz&_}Panaj#YRwLPwNPnNUo@Pu<8C}4M zix#S1xik-j?#h%>Vr9qx|Nj@L@vkUns5<4bYj5#)mdN~e<>=%E%KoR}{28l0$Q~z? z*x`mUDP+CiCFVz>d(S? zv!+WjL`ZScW^DzbP@EiU_bF@Zix>dx_`>8Rf|abTQnZSO1EEL^J3zmK31sl6g{>{u zz)@%dyAHj*1#{Y^4y1{{i)%RV$etOs;=>j*(|rG{QJ#2$8rOKuy_<8fO!S-ir`1?h z;~w?SKJJ|rIe(ryYOgUCfq~F@>?%w~$Hp$#eZ@!3)v^{eznfhnWxVt^|LoS0X3C|m zi_NGb4;vp}``uY6XbNA)Gkj)he4P1zR`xkpNo^Xg{Ws_6!$h0+bSC-|gGCEv&6^dgYGxYcv9154Gha6+5v}KF|J-3h%2eV#RN0m*ztC~z^Y6*6bw7KPleF0EY&TW=MTv( z$SWllYL~w`tB}R5N)_3`4%dyFH-n(3LT!SqOzGi%@ZbUb|LN|#07H$*8F!QMz5bP$ z07LuV0$Qw5!Rv7$br3w-n+C{dxAJcwqwRCg+1bAONDzM~#1K9$Zt??X4ss;IIyTnvH_?8l&Zgq8a3o9%3 zbB<#V`i^e6xzUGOHoGuGvq#$KhDz21n&ClHIbWO2Tq!|{z+Fg z9Hb-T&_Tb<6*m(F#AU8?e(n_C$D9wZSqmvUc6Mrs|3^x&tvA{K?`$Y@@)|HiNeeaq zd{D>Zn+9EkmkfDaWGdrWJqCH=&qL*VzWZG_Wf+QL6#7ig5e*2J-hzKLw@8Z{|;_9=KcbUhp&~anR)G zJ_pk{Y=zCZOKL{I>fxd6r%#`NEP+Rr?cq|O&-i{Tz{pT4`fv75BSD+5Z$V+?yLYPpH`+l4%L6s}D+9T*WUU@pF?YsZ2^7JiosUx& zt%5{=;JYm!09+<7L+Q=kEp2#irp@AE^$6#RKj*$u#J&-iLw!rrC)4FoPVcV_yA*tl zL19r*+NhnzSj{|Y{mFmNp4Y$K@3GRp$GGard_9?eym?!^W_08`MC1*j0@^vaNyWaDFSS3D0EL z_}}?Fr;gmf$NpQ9gS?vhu>|Pl`Ze;qHRJFG)hrIO4j9Fn9NwD06bVU zvBGAQPy9~pwx3^>rEIDRa^J>r6r$h zI){DU@}YUC_@w*$BzulFFR3k?wPMfQs+HXvhN1R_`Wvlrobn_0p%vj0O=FUCBVhJ` zF}9~i!?n(gH*x@mM8%NaEA3txyzi6AqGGR3hRZ2DA&2d)UFh zfJmqcak0wM?2J-Dg~%clgHqn$p|OKf;z`?@2L`AFx?5XYv4KfItv5PTl)k#V_@%5dDi=Drn$4PNXT}QWzoYxFX4SIl0&9jcdfyDjA{D%k;;d|D zBQkE!ga(?$_l>P^tTVqM1;pCAPcK!F(IOi*gJH<~16o5sNC?oDfPjF4=Xq1ZQW6@> zR{!Dygz<8ySo!vj8liw57hoNazRySfjCC#>cV*mwu!NKiJi+X~IpOvfv=MYEi!|s5 zi+5Owyxvo-&irxiPo?37Fc0Tl3mpzp(xIs?@tViv!HC<*l`pY?{R$8Z+#ckl+ww%m zuV26VJU`G7T&K0dX&j9esU0- zw0G@QO-%$exI9T%*bBAGw`W!lvH*_Q+Tv-Y%XU1Ma$A{f2{O`D0Olh{S3XBKB5JIX zMIUq9(J`~%uDS9KgpJDbGXacQ?kZHK7!rbWYinyQ)eE)q%|3s6Cjpk5c>S3KwSwK4 zrK-#ykb~%oH2UyB5c) zT#gP86w>T|+?7z*(7+qSA*Y}ygSnPT%u!(%6TB=Kwuf=PPpig+FY2ljnF;+Bo0^&$ zVGcODlz~?fC;z>(A8{y_MW`USz%Jtu`m-Ltv%xS!(%dJDmsqY#VHP3SlSK;sl}_lT zw_^1Gi*W;+(K0oVajdeevN9A%)$!WQ)6b|zh0xpGFR-pURf5TKG0i~E&2$N6N-!k%K$=(}dX>O0JM@2Lv zAZ?=)9*(Zl5YUOLQ~+F7+Sm?-wSH^D%O^d&%OlR?ll4|@ z-T1CBio+so|08gLHis;F+P_y3srZM`bm2+rJ`(P;klSp%?3lF=zmX zdnOrbAo?7uNECH|v0}{@@W%faGm&#paZ0jhJI#y|*22;kenxYo^ty6|Nsp@vje4-O!FsZ|jjLHLIgT=sA7Nze-4^HgBq(xwIp_E1J=` zT1BG>;2yw+yUz6__n3L?U|g_D>Hf}sx~gQsCv4n!;;5I{Spjcu)fx)7o*^(xnL{gx zl!VJq)_%M%!^6WL@E96w?C0ZWJx0HLY2DqGEHSGKb;J137ZyThkQPg_e2r-y)@x+P~6$N9oYAk#=7jj%@XzeZV7vk`z8}k zJh$zyuO4<#6jX0*a02-F!|PK2X9l|VFj~cFnoMT%5(DxQ77?M0ib#-apVtGQ5+2&iN4Rre~S2Hr@k+G#r_0~lW-pF%wJ;CuyKL_)SOiTg`3*DiqM10p3V&}i| z=;qn{pa0DQd4NBMw;3Hj#ru7=E};- zuo7%k{rbR_$Au5O8N>WJ7FvB+Acv1@$9m)EuU|WU|GsNuY)U!3e(!4Xp1QPq05wIZ z&1#*U_Pvx9^dQtOka(n@=6*guCp&TUgKt*Dcv@WZTZ8y5Jih*BacR0V$&N#@Arb`l zB?Hd_0>o8vJ6s5KAQzC0m=TAE9R9>>i=SU!!?4o&k-ovA#Mx8z0!xR7zRp)AymfR1 zlWGdpT7?C|Dfk_x#frBsCVoHlhWs8g0U#b>QY4E0!C;Cp@wFusZ*)b-VhBX@{?RjA z-F-5{dFi0sRxX|zQi0odo*%S385#hY0Rjl19}7=UPm3lg{ufL^DGw#?7z3RfRQLX0 z&;!^qT^|tVMxUJLhS+S&;P$=xGQURPzd)FLUqeL&UYjs`LgW;Rr)V!~q1Go%@1W!& zZAl~LV4l152Si6kqm9Yzf{RVZdCcm*cK-f-4lv-?IyWF*&aWKs@?#Nbjg|mV`m0R2 zZ)e&7fDX09?cuyt!3Z6-?@9b}i?c^s*N8Z>l#wDBAJ7^>x%N*}QiwZauJV04*6k)| zl$P1s-?f$vxTFSLrF@P|BK=)$4muudVt#94+dtC}?I#r8o}4A9Ahj$~cUl>gE59`y0^)bHwp@U!~) zrq&msm4QfkjeY6fE5Aav?Gqw1`|B(jWMUJhe&*VzIC%CKXazYN9x?SdZ;$vcj;!qT z`uTG9hA9O>@gVz9jf=4P*5@}=^(msKv)juBii?XUA#L53##_3Dnxxkc>>kN$q`uHf z#Lmm6=0~_#P~34r4pj2Q6ql5=9+L<__VFdS_m2^1`b5Us`Kzs-+yH))@Gm42H)ISA z2nxcH_T6VV7G|i)qT(4IT(_Mc%m`n*z1q-gZCwdUOeQ}yB7fkZ#;aX81_6F((u#sA zErSB?w5X`a{r5K_$FH>^TE4&IIi!BReUH9?FPi1%&1OJ@hu|)Qw+ioSjeO=|^-xOO zD>)sjvBR!Rr$s}rlkRsUH8zQ}q;z8>kD%e~8&ta6OZUhlZidzyR9f z3z-MN1zM&G(uGp0NyG#JqDFLoF{g=kujK~%u?Qu@z?9PY=QU%6p5FY7mV$wGz0$7q!Vne71P2g zL|a9lZ>QM>YW+-#YIqnV_aQF2H6thmoCVjM8an*Bxq}EDzd}c?Lo()jb^-TBOY6^v`si z5u~RsF(f@kX!ZXHSw7S51q-`1vplMW`4W5sp?ohiNGRxL;+yWJSeqm_nCwt)0Xn+_$3Lt4YrriMxH z8WaiW|LE(mJ&m3dWjR&PytG>P4%DMUC;Wz#_r^eSghBc2obu-QscS1otk@m!I2+u+!GOM!xlCSihB?V>-}=A^HL+49J^r zvvMY7@;-g~RJ?y^q;k<>cjzC*0(c!TA*_1|dbqjr-&4`WhvvL%GQ2mP8c zF+i>4A9uezU}a^svbQHn$(@>N3j=?Vh;&^MldE>yL)G_%gNAxU*P~$8&lq;V>k)4| zaEk1hP7p-ze2%2J>cj5&<6L^kKV%kTR0uyE*7n`Jw14ciDfVk1b|N?BbI+y+TC1zzUNPgkMrQc<@FD*5`U13c~h{R zZ`0G1+h4i7Ke2qk3bH7u=5%y)E9*euZFk9a^2qJHQ&}18u4>#kyAQ^nbEYF5LI(pv zQ&;^qu4?UnKwM`tx32Fo0?(EwyK!d!UHU!|YBKh4E_P+C>0k`ykJ*mKGz;yw3XwW5 z{1k_+J=x)Z1KnvmA8%_sIx!JiScpJ00n21OxC69gWM+nLL27yNUTY?B$tj`JxPA^

*l}vy;YR++jU{hkRpbGwk7GsO8Qb1#I@8%F7dxkzt97C_q8q0d^|Xa&ixI zBnu!MEeMn#&UMugO)KpaFf_gsna}uvhLvo8xG5Wi*?Ep>VcTzE!zGabtIiaG+%}f) zN)X%RGt54>?{*toD_Ud!ed#_iJioxX&;k|GT)7pEq-y3^3zDsT2i-D3%0d6Zl!^MmqA*dmV`q;#X z%1v|z9jlPB3%F5)+0@dyxvRMefxn`VV&~kdhXs`n5(DfFmsR3u)?G$HKbxO-u;HSm zoSv)w_*3!;(aft>8xzu7^LkAJ-I1woy}i9NuhS0%WcNPz;#;T9P6-`O3$Yhn{t8d$ zXoceF#EzNV;c6@vN^X@}4&<`cic%jkxwZ3hK|MV^)JG2hLmE9(0D9?(C&(ADx2r#% zIZYpIVc3f5Qb4+Z=>|q+B%Be%Sz7;_7y@;3^TO@1n7yB5WL`rja)R>tv<+zE476n)}b3B+sQQ#--vw=sqDo@YZaL7H_*fM*iQ9 z6e8e?Z=O;vyK+r2+WwG)G5QGC_>-{c#&U+eSnrYYnLkZ$&mOSpbyIY+*WO}PS#@>z z@UVetlP`zNo5G=~O?Js0ia=Wms&iYO9`QREadCGi zL~UD9W3BrSa%ta{Z#?)M7B(Ib@3a)-bHb8C~{@coanE6Q-W`CBKi8dvEOxq z&sm}2p|`X#UNxrV!w$5=4x)cOT;OA@ssiUFNS4ArYWu41Ll~pO(0HORKe?5atEMpE`wA*Db8^e?Xe)@gqtyF5zdLLP7V7%J{8f%< zjQtAK5H3>?^uWRh5z z#Qx<=IJVs_)Pl>SN7R? zGu^v)J#r_ROero`R+KSXrL<(tdy3?GGkxPq$lFn;gHKL)N7v#wy=Ry(&0>wK(bPwq zSFZ0;HLmB}t`D<+6eSZCuBmbjOhE^Mht+Rxm(F?sb2&OX+Dsx;?+?>~*}N6|0xeJK zE~q3f8+f`jz)O8^e}1dKepHzB7#fm7hcz4FIB)`6USXbA8JphzfW8in_@iv+e9o4w z!K*&rDV(CzvY_O&w=0Zm1%>*{lj*dE`*&6@PPX%s!Q|v9;}F-_00RpQvOpen-s_bK z1B$S!wCMeV`G0T1B6cPeuL#|Rw;!N<$N0Q z8Wp?CuNn^OR(vS@NmO-3L{{}&6 zIUybs4%2cfU}dFZ$c`a{z^=BP?@Uk{iqCu2@kp0p@=w zPu3sKbtc1OCR$9Jmj-{AAu<1x^&KhL;)aHQ8$WlIMm7Riaop7-uE(_oxQG=_Jf?R6xCwF2*94iL8+_((h^8ikO4?YNXooVuT3A_ofcr5^-@&0*^ee092|U_mPX$B ze_L{hoIAbZOz>t3@;MkJNTwNN~JTi&X~(#!**& z-j{#-Wvg(;tv)dK^KIb5W4>jY;G$uu1S>0$9<9}QqHV=!a7e*|Kpljy$7#4(ogNGH z9I_9AxqEDnS5K{>pvtYP+t>R?s7DAx3z$rE?WcjH1rrvGG>?b#K1zPvX(Tu$(Q|ds zp-(Se|2~yJ38w0_s)V~aJ#ZU3-1wUe#!s?yYOwG{jX|E8i`0BJon6Ss+;wu-It(WT zj+Mw%0`X9laf5*;E!Q4VY~fc+%hTmH{9N(Nd*I>(uz4v*w*_PxaHlkwLy<$BNY-^u z$c4J72<%s6VIg>46{^M*;W)#~YgLnbvVe<7+X#0t;WMcv3ojm-C}pksHKUJ+%1zsU z{W=Q2yR-PTinjq@X5&nf7y0YgNP9k%L7nuEolV=b<$b@hH<4pJz~>;)&G_CSou=t{ zH}9EKKyM7IRKHJ!!)Q4km?A&#DrsbHt_0&a!a_<(bxS=ugJ3b3TrLfwk*N_VbJ$(6AwZyKk_1G5hLL1bD@NtzDX(2mvZ+G|bjg2GJ$N|sW z-=g2{(wV$Gq6IflQ-+C;gbsbJy-3sq@A>mbXVr4Vro_G8elOujpFP(*tE#I=*0k_C zT3I#oO08YIg`y6=xla5y#4d5q_w?)n)7fDlly3TLPWfQ%174;7Af+mv2z z-{a~29g&;XxU8Jw8Cf?F$Emoac9k#T%0}>H<9W$xDSxFPdal(PmGhRyi9s`TsN$D0 z0Rm0f7XxfR&$-6Ds@s$OG;;QjrP2IOC zGBdd~u*l3(?~K8_v`tK~BJ`!#)>7H$+r;3W03SeBznz7prKO$Tpy$~F{z4}(jv*e` zt*w5?sv!J~DO8wVI&Tqh6nW_*z7IC(p$G51DrQ8{*B;`#9_XRc@P_}GFcGlR1Y-%X z)-sLl=zdc{H>(t0A?-8dB@5iSnLnUW-7UHJIWDRpiZ@^vAX5>V`o_h0S$c_x`^*Fj z3kzVnZ|Qj^whE_(L^4#dK!C2TjYPob=Hi(9&i5o`-!g`hMxw1oN_tWDdSlSYi-ubf z*EQnZQzR2|KC^hd%*d2WQ-Y2Y`z)??w;p{58F24W_UkrUMI8`(0wN;j-d~=w&jht6 z*S>X48rW8%B9aj7ElNf{IO4S6Ij)6+f^$Dg<+TDGz)<&#T!p3jdR9Qx@sufS*}H9l z;T0V60P*1*4MMi&{MO9KA9_2ly0W@>&x{_qlZlDWheD0v@J%j3qlNoAYb^w9lbz8_ z&SqkS&M{hNPWTB9?uGI?8p|GXn#swNq4MWR^*84E=^g$d%#_pg-~fUpgB=`3n3zz? z8w|+geQa%3ps#jN7PIGjm(#wszrUX{D1{WxrK~@e$3wT;@;Q_<^hFJ-cG$~DgETuj z3Wm70znG6J*R+x?j+4xGI3oLmeDv}j&qNja&T9d%gOlax{v4lY>T((Fi`Na(+npYC z(mt=w?hQveo~EZ$Aco7B`^Q2Y$H45BVDTU$TPOXv?cLZSfPju@MxMf6Ua7dUB+3rE zKCEcx$B~FGTU8a7kdW{J7v0=7mi9!b+>&~Xe`(YsEeuYdz{#+gFBP}kx4ycaUo}a+ zD)9tw+UHq%y4K>}1C!Vk^I@?rCeNj%rOdC6%7#vqegoSk+M6p@qOA7B#K#_Fo9uyR z@D10Qy)k*2aTJYIRbyK%w2-EW(erG+7?-5%tw6o>^5~mp=*m!9#7mm)xg}JDpi=p0 zLScYLFld5LPFPCnq1%K9&&KypmR3@)JGXqKJ$~atw*z0EQ^N)wn7hZdZgFbfn)BF? zSqEg3G?+6Qt(S9SV7oCrB_o#}4v+BCz{Q>rim3V;;N4*c5)3?$P_+sRm2x!`)N+h^ z?c-?3qv|*fhMed1D!HtMI2d185};>g9wg!*=5T3y@Nj~w964(SbZPy@r)$Zkn(M_Y zA}Lmie16EBLFMj=&j+NI?FW>zVRz!e| zg@sQ4GG_yazut7x!035yWNz`5f#2h#4zM%2=nD`lAFFW@+7|rbz%-)##U|W22A6Lo z@;Lz4iI8ZI8VB5y_dZ(-R+xXw>*{}f`a>p#-m41%|8<;>1-vA?SF5goyt8! zNAvC9$V=*0VLk%l30SsYXL3u#>5KRRrqlACrgo7dV~n$bK7aY!o-BC%7iyukgFjC` ze)5lI?7|t#k_RJI<8qdHskMiPgx#p;+K(RfFbJof;K2{Vq(E=BlnWedm{K}z#NtYK zhFV(ehs72jFugQfXm=2pS_vH%b2WxqD&i=wA(R&~Jf*a_;mHE&`Ns%+90O9@Bn>WFiUibrLm8s7+r7X9r zGmjJXQ01PYM&2L-d7)}9eXR>iPLA*HxWSc=HC-wVlvHiW<&h2QqO$P1ei&;&`GhW+ zFf3NtKwyDWc>ssJ7F^_>mp#fR$#!YW_~F{XSb^hjYuvJ0^7nA$pJOY6uJtnuS`A?T z0=4Ohgesw3J53%ON&$Y+={93Lu`#JbnygPG*xJCStDZnvUfs};Es|d*K!U)gk%eln zEkRakcjtK`7dpOBe;Wd5U*IG^Hj~pAG*UJ=#obl@jF!9jdgll`e`>`U=lgGtC*4jR z2XD?nRCzPdm;$^3h}XHk?kfDl!`!(ZvG9HRWcbaUe#wuzApsdGR!@H=1{wR@OQ;$I8YgUAQ=+`Q!zx-^c3*gXN9M(aDbPxFk-U;0;d>!R_slupFKS+JhzZPOm3J;17~tO! zx$;G#ufJa>JC@28q-r%8%0BTJ925tI9rQn7)0l}6g*0$hRMW;-(?@&Ip#n*Yk^5z@ zUY#gFiEHXSY4`Di@QMJq==PrsoR#om?C07bwo}^Rx^I4wBfn5GQ9in`uz<_~Xodt zv;x?$ZH+m|in|Qr5`W;Q`E1$xoPOv65L%Z9n1M$*XD%ke`3M1iGq&NE`cXS0N1?wP*DmhK$OhycE;M&hp~zI=&9-=f%RJVW=; zTWIe|HqxcKmg;=RIjyxgYW=~Z04NgK>uKuCKX3<_j2AsN-wCMERp*+Eg4-ZoEP-cpZfhmqv4}qpYi13fkr4Wd;H&Gf# zXF+;JxAHZ>I+&5cJ$WUY;*uesPI);?+g+0ljnV?#8q+0(4YE8xP=3gw`QdO?%3W&5 zPqN6#XHcDj#r{<#Q*sp%_o>#9etI>R{l?2|jke4dz#9<>!TvwWY0+J0BC*FR!&XS+ z&z_-QZ!k&>0mc$(p;eAwTM>Z*YrU8uy+F$aAC=kide$Sar1?IrF_Ac|CCl}TK6ZC` z;QTW<)6yo4@*aOX=)w$g^5arz(GrHCzG_C-iy6lr9d=9>a<;bkR5KCVT4aY6=PN-P zV(T4abzQ|CfIxh-CFrNN??wm1FU8UHnN|k=5H~OY@Jg^vI(RxluaUW{1*jhoP*x+f zys;tV8AqS5x|nR7iCMf|HHLUm>{H`#UHVxjVM;_Npk-5W1XLYV|d3X z-*5UU{n8d_bAOJIv+L!7)ynEplqC#28d?3}#Z*e4;}^B4bJTRPQ|{JyfA?2XX`{)o zRMAz0_jTXj7J)7f#>bv3XGgqsRKHAEGHw)Vfx8+;O|Ymg*7^0>a(WmPbz3O*IKyEK zkeh*c5mkwEy|EEJ=XGAw=alKl#fC$+zQs?d?Y<2yOV8b=P|gj%$8gT3GsJTLd}XCC zLF73sRo%(jBYKmU&v=vke82U6f8%>SW=6pigZPB+n|)i`QL%Wr;CbWHE#S@Xk;+L* zT4~3FkC69)F{xcJr(t5$B`R#be@>zWAf$JHmh$B~lDObSmJl60T`(ws;|t_8;2kb3 zVVdOPtXng1VyXrCK!2Qplo{N+_)SOfDhSl)@1F+G*@4`vVzO^*Fz&Fbucd4A3S{Ve zW-Vjh!Lk*LHntvd;zhFOS|?hj_~BTrgOmRcEaVgb5(?~wgfG*jJ+MJ6$hf&S{l+Ja zrR(I}{Z<38vXOZci*rG#kJO06PPuyBhZcN8`!1ao$%T0OKhhMfG?TuvUJ!C{ zus8RB9;Nd546c1?W7*;z-uJIb#8u-?cPox=9wzwm1f{sUgOSIlohAnxbF{6LzI;vl z$+sOB#zv;-DG#nqa^zmUCDKTr4TCNW=6%>o>V$B0)0_);-1ND>%GmitQcs^Uw!Qr_m zzd+jqRD8S}miwzk@D^1)>J>^g@u6;fq>z#DD+R61v5BPL~k&j1VH!ZZO zDH@luuD#6cCBMVp)dqLejMgKO*c654Ecnz7<-`Y^ZMA;IO)NPAAn)Q z2+sclFi>f$FdxkdRca~!^rBGj5$8#NQdPc|N--UQ#-Xg;P6|&9$gM*Q8*kBp&dbll z<)j+a)xGH|8^Rpjh#D!Lbjx9x`E95*#RtesgFQ)6{3%zVWYD#$LoxI-KVW;N=De5A23kKFh7;#}1ToHJw~b z<7e7WZZ_a~H<2m+^eJaC53#**gBwnk(C6A`e!gu5upAH^NE`$0dgCE63)t5ZNuEAG zV4JLsj`h4_+K>c?3EaV>7*?oh=`fGMMsfaZYTv)Dr2LmO-B-)N7LT zmY;@+Vh^xQ)4q&o1t^@n@vufPX8;Wa2ZoB^3EA4dt0ru!0#B3s!Nv%jgtPL|guYB_ zC6x|H6POn$38S(}d6v!cS=xe4B;AEA8v9T?PS+d@?ONj@ zthO7`obR-U3)cKr0%1z$fDv_hZ_f2lZgf)+_)vJB%v*YllD=XxQcD`@>I#f=p+)47 z0mao<1?WI-3%NA)zAHA{VCa>yMkSmC2q3xI2aPY|^X=)01}_e;U(mqO%$dXhxD^_z zW~knpapl`zcxGg3ii-kO@2FjRgY%Is0KcvSbV*czUilfdlfXo=An+#2bm>b%VlkcO zt!a;a!F8A-YD;)zaaVohbOKtcRZO(?N{`NvlOJlK&Y>t?zdn-5%{zKlole&=`KIvl z@qxSl@mV44$ccp=UD|WNeEfI~%KjcSwC44c>1_KiD!GS1CFII~J&=}`CM^_ZmGutS zEkOIiTXPV8SOrfIOqq;AEsj z9n0CURC$0K7UmaYy5yk}M~tb`DLo?M=04=lpLZSCa^j7ndy*3%o(S$^)4EL;>U&)- z(074kJ}&QHuV^r7yuS$+h)vj6eyJh?fRLBLSwq#w`IGaezQRU_ftmtSG(H_YJ-{|| z-<|IYhL%zB-TFh&u8-0i+$bD>)h{NrFp{<}bTx1!}MI9F3ma9$h)li`~kvz5kf_{@EzgwSq5i zbvhT^tvY@i#jv{AP*Mcq63NO_Rl8%lKFj5M;XT47ileWdo$!)1Ftfdk>n!N(vLu69 zJMi&S;f@D(7fg|@&CRHdCafw#QBwFUn%Tz3wkKio7|i)SpD#EX zmjX(G+E{QYe8fIV>QwpSp_Y?g@cinmN?!+bVfq#8N{oz&YgH%R1 zoMCB+1zsxf*ffJ3t$^N+bew&t`nx#8*Sv(iT$7h651)3D3q()%s9qt|e5m(>T1-A( z`;;oji4NKPc{<$F_!`~vGz6z2XMeGpbY+~O?}&awRdU}VN%ErLQ|2R1Q8tZtkZ70=1#XTfk44+4X8zhe#)RIdsP*`}O zqE}RnQ1WNv3svD2Dzwj%G+XoB9Q~`xO|Bsa`|Sg22G+xylRHb5_DkIA`DFxYv(GM# ziE6Yxb83vZR^^!7x&fy<03^GbiM2f>+tSE;9=756b@K*e)W_CfMy0XT+@4B~VOBo{K(H2wqns>*eCAfimom+Ox_4&NW(OvQT3-b`=aVp9o;lu|ly)B@nQ$3#bZC;ZZm@y0UZ775^X}DB z^A6xuS^UZmxQAz7W-lC`r&CrJegz7#{9E|Yj6>#Hb9wac7A3iy z&DJYv9BPNTX6K>QV8fd0@GM<1Hs?-)!v`gP%6#wO@bav*kP@FeC4>@rGcMyTO56Ym zhY2|SZw={DWBSoW{&g1|fJAHDt1tS-3nzipuPi^8ayvnazsMSwW0b68&7+?G;+fj_ zuF zy}=c(SO0N#4Iqg;jJBr;-4`oH;00awHt!(bmEcFzqF>y74 zvSaI*>)xx1-kYz}JAnK~O(x$=YZ=s%BePicJp{r7<$onfaO(fV*;_|N*|q=TG((So zFj7(qBHdELP@*z|0w%4L3Jl#H3Jiz}C>;`lf`rl~0xA!Uf=HvZgtXMTZhW8jobPY_ z&Oc`@Uf;Fk5$>7U``Xv_sr@s-RApYf!G>wGP!J<9C4Ef#mzl6xOyUrV2P_9r&kB#& z&_2tuDq_-8NNGWYCATVrU@Yz*Hmkl9>yLqcam9l}sgn7f*D_m*0pxxFONM%wP?=F##iulKEq47Yr| zr}OfsimP|pqiJNkJA%pc?6_1$G5og6TLP8nsl1F}oI}eoPuX!t%yeg(-0`c1M`2nV z-5J?j25k$76t+kpWiH1Skzm9?8p*DqEZE{`$dkS6oCktB+m zg*;Ht!n=de8rc^TK@D3Q}GQb&8RJ)Nl+%$)=KVmoJ#9<#g zt|qyf!qH|FVXy!oodaTLlKEj@iAPijOa$Dr>CX6mx2iW)K6i9T zc+=0b7MHFHjL9D%ilJfkqZwaLiLMhPh-ZY+YJhQB$7eNZREfJ! zd?uJmhL$OMk{cEDQ*J9vZ*p^|y|sqB3-ch;#x~MP0AjmfXS}Jdj)Z@Dl}4Jv-l@-^ zz>uGj3O1V5k8d=~N=wv%@Izdf9uBF$X3?&#BMVSvwiI5<(%rD`E1RnWa|Odw2Fzko zOUIRaE8n>?x6pIz2Pn&8)U+t(C7IvxlZOJ$URC^BdlS8e{$*mt#`I7k3_1 z^5126It2nJ<=g7i6+;zI_H_{iTg2rN@u4-5dfQ*Z}NZm_f{9S_TV74S$%zQcIirZr=}>MnLC zB`QC^?}H@xZ*v^oNb5&q6x|mAjbTW(Hr*NQkM^yN@m;Xo>dRFjhwmMVjDCL-So!7P z&rSqb;sXSHynMJ}__Xl?3BvuO%4XIcpi({F?X;c4kdehqs6@v&TroW8T{ zgUYX>N?VeY7wvj0(M6(*qZd=}-RwG_cP0v+918pDftZ+BQbr>Mp#!R(9~&ZFOiX1U zg2`d4D_rGvl?x^%96rO7uSOoq-00f-A#hgB!J#M@PAR!?DkkiscB@!iE`D+744~Nz zkNJrmt*zSh<%`#J`Vj>)0(4kfTBZu>D^i!s>%#`7ZSi|$-#IK)Q))Orw71Bf);Ue3 z|2?`;U1gM@Ff3abjy=i8~!E0i5Bqql;wNAD-$oQB{IN--g8q-0VsZO68PW3ZHnGV+>$QXs&_aY@dA9caV7?yr9^El{ z@}Ox(*d_TsZGZQ*i5K$uCIR~n7N z3l9gmsdjsNc*?aDearRC660rjO^&CB|LDS?xVE{ORbM~qbFtr5Y6lF9l+eRKO?}MI z`LPcFJ#(;dqpN!cS&B>5cA2TnwgI)BX)FTa3B~!!i9dnl0FkWjySr)Kwe8(QUI9$zxW~w)y}nE1Defa@_Ee8q2St|>#ywCx2tmplEB{Chyjpsq3HP}&d@CCD zOZ!hci&+Y?5hef}MN!1iuUca1jDer}Fp1a5(pTDDZbFgau1+t-cbp3!XAy|F86Dg> z5R-z{J&l{MlPGaW=JuMe!VOO^p13y4#Mx!=haXf1A#7n%8I$)c<>Rwp4U2`$a)tKB zoK0xsi9N^7OpO%w`vN20x_KGv9;@3*Q1!=Gw;kudzLv7qI2QDj-FkGv}EwHh-2yGfC{U#?pZp4~>#c^Jy-?r<=Jt1alc*jeSO+)8qd`Rx@mlu&@C70)ajQ(E2mi^AGA(F`X8V| z>X%|kGfsTi=kJ<0|FY(C;(ls%^XQH-~-jLvgZ1=pt3QyNp-`ifCkTd}*D0vF8G&5sMZ# z8zFVhwdQ0WDo8an0BFib=lJ2Xk&u!3a=W#hWbi^wM^GFuvxuBCZCH|Hm7X&OS{OEh ztnpn!^mDBZUD1TnFUarSLE1c&C;h{a2WHC@q9#6EkELy(%cRm}=f0S?LV@%M-E$N+ z^{SA%+5YZ=;4{5R+S}J*JwOmBXls<5e--q*t$U`H8$JUc9jxC0a5qAX6$K@wVo5r_ zT5mGU`Kw54quD|$0o!iw$U#{@%1}S*Z*f=|#W7_D*UYg5WrG6ztHON-A|G_Knt{*~ zZ_*xTR7}0uW*0^_OE4mf^z*S_jW8R;F0k5wEC4^jh&kD}pf*ou1^Iuxv7r5Fh2LpW z{Om;0g~?(!#%eNJLqlX90IuLxr4u>$06F&-hA(ruVC>IWKS!!VnTd9FGagwV#Q-3m z&nAhUq_1$`*HQSgYIL(nfOeY zh#o`FXWuN_#M!!U;Gcq0X{_S2ol{AaxQ~z(@oJu8{ZbNQ@oc++uXF|eN`(*`@J~S4g$UsOBjUj?B z<-RLUBcIAUli1=mlc9%#3JtrZ!2|_y0LsqC-s7PU95+&Bt-1QTG*fVpMfxU~s$sPNoft>^L@SCsi`9NX!SIg;s`2Yr7RHBU zUf$mL%|+vzq(7eNIm^St!At-hH>fO7;Z1tX&B?25>Z$_$| zQ3&Xl&x8aZx^S36RJj8vkNcoA^gj0VpQVEjImf7g=lMklEikjQ`~1&?0kJ`uuz={O zD55Php4%Y-g@uK{TVt7n$OTP$?8TEAwuz?{*bjmlxtUT#8rqVQrPZEfolNVL z%4p$(4-+4N8~VXc<5om1GrZuFoi7B~7lKs7z~(A)dqcdYEU&yrAHtU=R^*As{;ba% zT)%Z)ev2zVpEm_(2|zsbKNWs^UcH$wQpnFwVr^_}KKk6!XLjZ(t9QxQZ?H8~Xoiy{ zY4*c}zx!s=m^>A z$PgCg!n`vkqbw`9ippmewY1Y)L{+o0vWyv?E7K|_Yq*(*3PSYVk(gG*X#=P>4Em8; z)|@7cqx9;~SBZ&P+Sb7Fa|VQpyG?ipF>fTM&FU)9jiu8z{^&ks)MW#m-_IAu(-pR_s%b4Wd> zg|0|7RMis37i*Sv>+NBfO`yvKy13L(;kDdrlV8Zfh1iJ8FPX!_!VWk8@Y|N2mn{-t z2KZRwW5!wy%OI(pm+>Jt2mSG9O!;4=KmR;V+=Zqc!E!qi2A!iDXQX34cA7b_GBn_o zP~EBzA{@7Pa_{ge|EmRnjqjnM4=o2@Tge#*s-Q0Z5ttqAIB4D8pHG`n?UlCsyal?i zICf9kd2=c5tkald@CukZ7>)a5+pQ4M&G@*v(w~{EzTrNXCLIl$-0MGtB7&eMKrhI7 zLq(m>#HH_^g99H|3?Jje0j{Z`bC#3|EyfElUW2BH64H;LPluftf`%3n8V)iNpwr98 zHyMH^$(X4}iCcA0WQBts(zIc|8d+yaRb~8Xf`c#dpr4g^Uo$XUZyAQ(Aj`!LHX!_v z#2g+Btt^$i3C%6*Xf_2s!?=iKRWq@ymXA%A73@_;uCzikAIKk_vD1K**9#_*=pZ15 zU-a7E{4QM{oDh=u1V%<=4TGX?Wd#p<Nb4gC+u$o8yo9+C$EH?JI06ig`3uysB6I7C%dGy6A1M+&?72^8%muwQ$5sYIow zGJIENp6hb0J?$I>c-#E^T_Rt zIudW0XML%YA5KPqfuPJ9+)%46OllpfKuK2N&iqokBaZp1iYmU(2U8$?1n`xt=-%Pu zQ@;bAS8TuQ)hdU>#*X>qKUFH8lnNd4Ve^3c#znXXZ+GxsgEIi^KG5Jm4E5h;k%T4Y zjg)wX6+;vGTPVh>)16nY=X@2l7xe`T1;A~H0}YAgmX`kUF===5_hXVkTZ4inHGLx@ z#|Ij`51P4x1s6kERbom)GOa!gOG1lQ)8DsLBx*2-n!*ZE|T+sJFUcUI` zNmE4HyUFA7j)aI?6dzEp|B*!TEN^RZqmG9#AAg6EJf92k zfe0A~tKO@2q_6e_8=<7FrRVji;l z+k1XO)1rxFBx34MvcjljMgpUyqQ|}Xb)0-FKj#AA}K%_OZQ$&WOZAyrWLZL!Pe z0CbR7t-V{y;J|=c_7$%58w5+s$<5KF=ocBe(@ir=2jtb^%`<4+5xX0(h#^`YI<9RD z&j;pIXvV>A2IOv@%cxD_)C_^NT=k~6z1-8=QqjnON(%4uyzS-|IPzo9@pDJ;LKoLv zCg{W)#0`GpLFK4IKqWy$0FVvx&5@efttH2^_Gox&WJHJYW6VY2LXzu^nXeN6_K-1+ zjcTiSLqq8Ax!WX5zP);W+}tHal8hs_x~=B()KuiyHj0dg8tVe(KudlXy}N(fje*cj zoGjYMLD7nblN-L|{r79OJroz&Ef?qF(M9tLG9-Zs8ME%t;w{1dw62=xX9KgfJAGBT1g3Vk!kU|`RKAn)UCNx&eMw|#Ovm>z1NsYCA_Kbs{JlGnAa(U{;DDj6n^!ii{|20 zL`-%V-?6texVI$ZsS>JSbVw=B$j|FLG&&WWr;(_ zTwS+xdvDs=tYZQWPbF~l@X(YqA12<K^m2%Q;S2B$MTaQuW)(K;sQZAHb33W@cj_>&xf1o}F`6B4l47j9!3a*K&Ti zsR~!z`OQlx&K*ETNz_Xo#%J`;R8M*P9inIHMEv?nxfIG6(|DK&_F_n3g8M!F*0F=atf3ubQB)>_AXj~KA*M_ z-m*?pz2Qj;Ga$zJ#pRdW&}~7V=j6l)rMEEk-mqXF`2SrL2%yZnRp)aIhDWJ-_1r35 zT+o&1;uY`2zx>trKd&o!vRY<2j1<0L_A8;Qkx&eZ!=(^zh`_(}r&OCEZ`BzX*=Hl- zs;xa}*k#v`GWXa@Q|~Vrt5|gXY+m06B*aR+D%y(&HNNS8$szOBK6^-0skTZCmCP^a zYcmR6sXIA_p#AqPIiBc88j-2j>l@MSIUv_=<`M`rI~=pXwD-E0dYDU$pUlwuix zi=d2vyfWOX7W_l8;vGa*Y@s zc&DBHTT~twAjro14ShcQxHOdvh0^hvxq+IeaTYQmXqy|vkEKZ(1_;w5V#bPWi7YR1 z*_@m0Jw2DN*k6_B(2`~g&Cg$0Ek8DR!@2rI>tno?<GRIL`1F@?dE?`jb{_9><%@7NF+OmU9SoACbS{m+lWl* zIKPtZ0%w~r#$jR^~od900GDYX9JKEfuzP~aHP(C6#(5st%R?{^SNy9Mb4ER zpMREIxB4PD*5&kM#FmUFA~yDbEADel#=Hz!a6fLS`yrZc{SP$}N!k*LJg(&Sy^DEg z*n`k5A3vTwyRw=r*!N>~l;ll9?(H`~LPVqOHnQfczELZ&5fWqM+}iIZ*AqdXEVW4p z1(fP(=k)d2C*kRnuPpVnp)I%9Ff>GPjq zli6uR;nadLhJVqT!#?Z2?M-hsPW7mcV*gFHf$z#PRk+thMSL*L_evuqaN!keynS~R zx5kl&|D?=SR+)e?VkztLwjz0Yq*>rn> z>3##k&`4G>$c--id3&*C(wznzDDd$BG;U>;W7|K+5B!IhLr3 z2d1>NyIE58vP^ahjgBtz53emy{aEz}4w1BhbWB`)d2~?uRUtPDdts9>&UKI0WOS)6 zmfxECMoI`^rc8D<*N+&WD?On3OA3wQp^7a^=9?fyOC6g!tuSWu(3 z%y*6|9jHaRs69~CFNv>JVX~`=Vd9kWJ2-l)ZWlq&uFQCG`uZN62x?4nW0*I%hVPK8 zasULZy_Ig$3}{kF8v~*6#m)Bp1$ME64I3v|aN9XckH+n5@Y4)>Lg-ACKpES=u^5~bB9wcj;LD@)*$62G5yF6*+;Hlk2@1IJ+Nu$CtYJjd zOg2eVAw`zF8#gK}0TVW(N9x%ww{rXnoCElDm(OTN_~_?Z&cZ`~{A~(Khd2HEl>w?| zJ&*gpx(>UhB``VWKl!v&AGA7s@N&2*J<({f5q`MUVI1cz(O?*6svO`_82l-pTxec^ zbb1SzsVIPQTK(ehLsC#YWFV$O+Di_Odj+IjelI~K1| z^wC5o+N=JHtXLEey!3N`lUydM)alkNT;&5@D)_0b&gI=apPSj8rUvBN_5IUC9Ph`k zudVrfc--%nZ#L#)Q|ae@M6powUqGnrXB`z8iSGD(qgj`0{gtn&N#?8oUUHnz<&cRx z-~7A(7k>yEGQ_##&Sw-AEnR%=Uwh-vZI1F3IWI!CRD!6n-CWC^6HpfvfzpSZ(dC`J z3g>D1phmN+qiMe3QMj&rm-G6}QgTQH3rvhe^e!n*pF3n)sW6zW6CW%N&;j(ROT`O* zK~#Ag&^Yb@?w%7zbia&F3(S#>$v*^bhq}RU z)~wE;+~*cKW%M9(=2HnqJr9;ENSvXqM%)oJtl(2{X(_NsM)EuWV+Ru{@K4{q`aM2l zhZ)v*{6hT#$Y0?SvKClvF!@XCs&k`l7#&CgiQ*LdIa<}wIM$CN9e1UwXkf1eZ*->q zM`@x)OGo!S=5FFw{t?X2v_RE#of>?^PS`tf^V5pPk~3j{pOUu^wmEdVFyt$3jRnVl z$@X$R;?Rm``Y)76N0%aaVMbV7uH_S?sRMTm7DrGD+1W|iQNkxNpj%KV($eu(xDAP4 zeS0HRFhVz@dED{YTeuGw><@+Y_QUZxtZBP@_E$ch1_Tuhc_Bj+;`G-xaCj3o`O`M_JyRTA-NVM ztK49i{4s87t=C(Z$;zmY!U}%{)ODkUNX(`dwh~{9ZcG6 z`MU9+hUw+t>z|&f@N=T!>XQ!^67aPuVcdT+%&(_p`3xN*_?XHoW-#tI=Ibj11WtWe zjxatS7OaO3IXSH>&#@R0fPG??lW*6bcv^e`nW@xg4W5bXVhO@+z(a6{K-yl_^bM4rUwV4UP7n7-@kkhVG_gXa*1SRAvoq;AoOC?eLyhZ-ax&a$)1zsrIh49U0eKDCm0OB7%o zupYUudn-}HxXJ?*mumK zS@3yBd9rVS66jJkVA~KG*{1ieDnL&k+*P?C%ismy6<1P+m`Pv;fcO6KM^@zOZAeEtjT=1=W#6{&=CLc1 z{eMMN_fziKKA)*>zs^aWpKk%}B#5>!$B!dHyl)jR+6yI}i7?8v%z7pZvJP0ettp%K zww>*G4xzZ?q+R2yzkWUy{LBJ2-tPWQXJbwp9`Gg9Q|U(qf+|G=20F^X23r143fe=d z;_Gw|CeR)u47yKZ+iF8%V><&U2P~$AK?Mewxhl6~x@Dxk6Cyc=C5}a5GxVMWSZZ#a zV4_uE;9-Mbr`~@WMV7-8Apx8Ah|!nV| zqYm>N27=6q*o0$?$nGt)c-rV9SzPn4pdu;%UA>7WvnIsoGM&BD?+WZcf+V}RJgr(( z4IhtNy;skj{Xe@G+g3l8vfH*PakWe5$Q$I8P7=-d*>dr?qA!znGC+L@C`=IXJ+AwN z*l}#TmX=@bL8eKkYX508*$%d4_#gA?uT_O`uM^y038fuzrt33r?d?O$i6H-2U27f< zh$&r4H~2#=A5GawDIvR2MC>s=NI_I(qE)=*i_YZc@FUBVH~W4~TGujL)HvOc$_Cu{ z;Qa*V3$h8_U+v`d0vd9%&x3Yj-n#4slVV#6vmLXh+W%HsArgwYsURcVjo+FmwZk5! zWqWn{N0_k&l7n=ncmx(lDCBD2yf*b^~uWhbIE7URX#d|Agk z-XhBMppPh_*HK{5Jq;M}1B-<%fixoh1#i>z{DA3Ty)VWnlD3z#aL;8@B!)E0<+I}` ztm|J(3SaE#&Ee(7{#?p5_;dGMk01n-`7{@%{l6MWMn5YuRMHmVT3 zdCZ!{YciV&EnN#WGgh;#B4yv>lhHwtvJdzLoCSa?y7hTB%PYe=N@yE*IDo5nH;pT^ zeZZ;mVB*Z$<^gyH{ex@8$8UnV=9^^v{=O;P{+E_+VoK7f-8FikgTk&MFIa6xQ+NXr z7D*V$P^QLMn2psFw|ZLFWyc{50MS-UL+4)hl^s^)i+Quc5hVgMY-tjm+UbIKH|X=* z?l~Mt*u5n`*W*d#VfyM%nuN-}!~J6n2uNUPm?TRR;{;J{QGcb)P`}k5zaU*qDjhl~ z?Gac)TEVNuC8Uq^?TcaG6cT-}1I-!*sRTrXzQR1TCF1xp7hOkfGdb0h0sw z>DxIBk?8`Vxxkp|9IVZ(30qAgtGm!W#YwSojG5?Jy}n^Pz?SL*!*4y!j5YLS6Q^jS zX|RTJ_t+WWJkvJ(Vp&hxoApo9(?E>8zxR$QWK3(ZNwMklbgj)H5 z^K>fw^;IBU^k#Tq#=Cp%x@YHNhAY8wi_6VzpRB4dVA5lzD(c)1)_d$R{Tu_*PVvbQ z;rw4%>KALK)boqq7n6KcMwzEN91)?iOI#^R=6|T0K*c**a$!(})QJfS(%+*RsxAuV5r0tY3;d;=CIE-oGbzRS*|Kh?s=2q$vR( zg?m_PR2fy+b4t5iN6LuK)*-L0wAioQAlT;vvM5J>tCsb}wd_(6XdpT#%D}b>y_S0?0m5FBc(zGGbz6x{v zMEsaK;a~!4qe{`K3 zEl60S&kw%i669Gi@c)PZ>g6Thd}qqfHdF{Oo}JPofJK1~4Myp%{{C>l%&J=6-MjOw zgb8E|@Y-Rt1+`fEUjBjb->{>j3@5F_3SBpq=J4Pq*S>MuabT zyTp(8OScQl4VWqb2IZ3ZrI4x1b@)lEE{(n8v;t?M>_}AMB}iCBB!!*(3!RAvuZ;@E zA-AsiyF)#s(7nxJ>;}MPV!hw=?6Q~X!Jqz>;Uzy-;2E}h66!*rl9sAnm~_5sWTfD2 z78MT!diP_{$=n!bILmsI&sragxUTIr~*XdQ>bp3-w z%Qy41`aMwAIkQC1{rR!EbC~2;=}h@of%*FXroj9@$0OB&;(}W1edvTph*BZq^s1YU zEA>X2C+#I*_`nPf5;u@@e{*r3Bp)*PQwB(whZ22SXTMQp;?*kcu9-Xt%L3sp;77>F zu(r1LypDz~rdw#p3GiO?3cB1|>L6GVWX1|I+Xe```V#&;#sRQh0Zwe##~^J2>LQN zRbh|&SiD;2mjE?RBa>u0d<*JFwfAP>ouXg-Hf}e|A`d5YqON!BpXKcV`%)y=rSZk0 zKpECEEB#lVWv%mC28Pbshc1x~OcuXt6Tgw@Hk;IAxlCZTHex1-F)#AL-uS!SX*vZ7SYcd=Z%*vX>j$D*V5EkY}>4 z{U(;shMZ~%Z9oZr01FplXQt7f5l&vngV_zNN}vFT0}o^?cXujH80~D(lTq|`u$GFi z(SoJm{uh~YnpS+@1AU_=GJ1;OZjnQSyR#pI{*~mFg%W2ej6}iFHe)VC3S|DbV z?|@NIU$oxUDkIEyV?(_+86w`4SXmoEGuC7Qgd{*}z)yp)0`XsSJo`a=^ z{QZB;XBAFAslkKbH%AlSAHt>>{r0a}qPHxX(xv)(UU84eZ8FjCGGck6q|h(imVb=r*Qm%^Lg#kKqbeXb^culHa*RJS1=B(@}?E zga_5LGcCxVujIWY{aB9=g}tiCdn`H^l32i~o3k5?8xL9CD!W#UHZVkF zYS4pCaj?M+!J1d3+3viZqHUpCiO?&p`cuTyHX6vimU)8!)P#hn>Aoqjj->wH{|BCn z)+|0%CJ$;O@B6v78Rt*4iYkS>{7F+aTqT0(nzH-*j9~Y zYyQQL@7){w{j&{Lrou+%;*aF`QtYa34k#RPTUQQgVklo{dwo8aSyQ4KcjrHjt1j1keGMyY`wDy;9KxXG z871iqM-%p?Iu*%!dl;`g1^ zS)aLjPTq_IMBx7c(b?GEgZaR2n?4BCMY!X4voJ`b#k9Ba>OH79a|8cQj*Jg;Mjz$p zgXa<%YSTecwR+}@-FibIIP9h}KcQ%q+b5oa*QI=%Jc9yFne{V_u4{@RvL7U)+*e03)W8{aS_CYp(7v?O)mHzYXVN(?078C-=RYQ_Dm zdc^;!G@32ZW`VZvZ#(sVY192jZ4G~)%V&M{|ESOmTFKtJ;hkrDbzsMPKvXSIcgv9a z0G=x8y-gz;4&d`>DUQ%mfVAhfib+vFuzrBg0euXBHkq1@-52(U8qu|Z*2uQ;P!k^W zJo{(I|H$|62XgYFMA$hHdhuauMq)Ra$QoVNj!8#+3=61%TSN4jlcqA<&gWus!{b!G zG2G9-M4&$tCYL_BV5t{VI1iLHd|@~_n}N^1i<)CWd{m8 zIY;8}a+lhuU%w70BTB*?w?s3$8O}^`SYQczU2B{$XaNgU9o{%2xlkC52WkVsieOJe zmkCiSimMh4^H@T6=6CuGn5DS6Vt~Iva=&BGy*Rxzeb8RhkNK(K*eJgCYPMbuG)1NgcTn;`FU*go6!yP)vkYl4`}9OHw-!&p zh)Ww5mO3{lkBb~;_oA)hkO{?R%yI>c(;-EmuLOi*phtlD?E1S4yb-*1Zxc@x{nUtJ z86F;Tg@_2`-0GhPAKao>N&&c-`sa;(FIB(6egL5j@;w6~_+HN9#DZoopUiA+XH{*AIU`R}Ouq8a%*%K<`5~yq(*}TWRp-M>47Q*A zFZtK4?);;fBAOx;w=b3d#S`^@Y$rQiNLJSGcyRCmhC{oa2zx~(;%dex{U%Af8mc5> zjJBkb(ml^=Q=P|3oF|8Lwe{cL(3E^0BHU(tci0DG0+h$kYjovWaC6r%o3!Y~n!fdi z=paz812Tk2>b~AfpM+ur*a>_=x>D>pvf$uggd>}MT5Mx?@5|i~meS#y6z%f*c|0Mi z{h)jL7;7mIlCI}*^!s;H($*E2!I?q2?Z>J;=ygkA@1L7L9XPKlz{@bYZ!zojg`<-T zG@e8@L^Emg(z>O!|9qRz%Rf($iaP_(pW>cGdv$WXC)fS~3;1SWMgrd?jN(F|;4+`5 zD}?&gTcI2EXz`xxh0vRm@>SjII)W2I^5e&kFRSvtspWQ_(mieAy~Ez+N~g4>p8cC?ZCyFc%rP@->2CVG!id+r zNLz4F^(VY>+Ksn!GRbmK(h+04>z=BOgsyo8?a~-O=xI)Wysg53W#01P>nc-x%i1aj`2`_~O=F%T%$Bq$9{ALA9@PMKb3WTCeaBdy zL*=*EsR&Z=4UWdP?{fiD4YFNC3FNwP?NwM#340Ot%Jy^NHh?OFkpiBV%_OIJG4F^7 zs|Z`QlQB3&H!zkb&@ioS6yo(MgpxjRdO#VmkV@Iq33Ts_0@vaGv)verRdG~G~U1b8IY5WC} zN9@J{K+Nw{kAlz+jYR|>Lvn=v%k};wR||c9{$p7JGAn3&%-`qdm+WXTU|YYnE;E~S z=1IH{d7a)JYi@Y85I1%rV@h6I7Xo0RTJQS#U%io49o#WBzQ90D4@Ul+W#JMJ|yW0{&*HD6YQ7F--mi{@-V2dzj0Lsn0}%_K`xUj&?e|GM~;SP-X=yb z`(TO(#M~SjMC8u{a^x4(+-yq91M)q{`ys=+!20+aTU<8h*DuH~_AkzUaR5p$;J02|9DGy(*?%(=P~{B zt{+S8y`vp9E$kxT#Ar7sUW~pF+!){8wtiE$f5(Q6Az*B%(w{_M=@t<^S&rlO?|OC6 z3__Eq#~Y0j_I)Mqq1mspa32ic!hz{Fg0$=QiTR7=%{y zKWH6gU@8Qywko%{)vPyi*&>Lj0Y88osrz$<<6&nyqb)%;i5$2Jt%im&r6<`GYZn+q za%QcbsR`g1?TFHgmf#3yP}MgAb>i|)Ys{{9y+4P#di_v#_uJO)LV9A3e)-m!G5MX) zP$S~G!&Vz6t0>ySmhA`ex?+c&=%`w)iS;)}uF&CQ5*XvDvSZ4Bsb3gy59QoG;*TB~ zd8BS8>%p}$&`tud3@WMCgEZIQp;br{hZNrPC3aAYDW*D~9L_ zZ3>94+w;iH_;!>ZY!hByiBpo;2hU}S{TurR@Iq*m9)9wl85K|T1NCs+a^tb#oogJMmwiZl>ieFt^V~cDpv_1fJ0{NIJDEm+j0+t_Ua5Ye^ z628mZtBas3B4}7se@#44VE7l}qdcDr8xLC2!@L7t7h?w}5;o4|R~$6K0HH2TEE3mu@z4L*<`EOzmx8O%&wdd7`n2?4*bUL>AvZ=k?%|&CSao^X%Til|&%bqO=+^g9 zs&;#3vF?1`qD;XYk5Ui{I#tqwCoP1fdQa}7nG0Lu^p-te1n;3~(FF$$EI%jJTApm6g>19pg{vA7N-- zRvLZEe!FbOZ57)suXV%zHXCv^AuxB=)YbyS<4T3iRZNbh1N=ISKxQP}3QNp+Zfp{j zu6M|XUTJe=`8acLK&H4Fjm=A*v<;lzR!<3lNQ&ebwhOr%z{6C}fBn?O$WOa)#*VEt z4l@$ij;6i2T%uPxs-I=cCi$Eev@PPYvSB-$OVt}KA4RI2wXWYdMA6m)Y4Q?6V6LF0 zuJc^Ay0X}y;~4yLJj~LDoQL}N_Q3;~$$+F<+t}yOC|>Fsw&&Zlf*?z1PGJ0l6Ce6G z_NfyT!RCmti@>=|D^Yn(nCrSi^Aw*#`b!zP)4sn&U6KQ7JIC*2w9Ev6(-^=QFmON* z?v&X>D=BO|RiUG><$wo;79BXVrs-9gpCdq4y}M>1j)~_% zy(gCVhSCR>5G)$alTRdWN`R;t#>3~Uu{Y(GZ9-2T9$r9-{`${emXF5`y*mFGqBDj4 zjdLNyukK)P$TN;#B_y?j_x*E-wz?EdjpLhz&q3M)3Zexh&w7#YcxR+Y5>;qDkhi9n_e zl6{Cr(L5A2&L|1x=%A(U=3L`6!-xbChK z##LtINspyAMMgtx3#9%7#0v3pKt^Q`6WdI`xc|u(YzXkwE(*T9{Y-ScTDirg1&@v} zCto}%0ey5Odpq4|+Rae5=t*K^pe1B}`bPHD$k>E-<>Zr3plv5sz4APW{j4QClUNWp z;uZAq8S;4r_J)dmTLgg};1?|IIdvCIK8Xa)c>PJlEe`MLp_shQG{Fd99z?qdKsRVN zYqqD7QW&Cl2fDGNQZqp|E7 z^l}_c=Ag748Hq6i(_F(U&x{v&U<3c|{>o&U4^~5812KoONFDE$Zok)E4Lu#yw&S~F zlvXzQNsCYPC?371cc9%+8J!7q5~m1OJDZrp>A3V(C?Fd07;2Jd8a1LM!p=#-gto(m-~Pf&awYfJ=P?X@_CM!Br{aBCn+RrPpe{6IJJ+^SgiJl8X}atZu6eO>mcrk1Cy z5pjP1OSqNHsq$X@^<%3?PNyiaC7w?MGO)S}Pa&xk{$0ur5CB*ppxHSz1l$f}&;SWZ z7h+&Nv^MBwA|fw>E8NHi(-JfaHyGjZ?H#i!#xXa*ufZEz{Xk=JP??#iUBnCSFCbp{ z<#M&a(7Ct_)Jt}62g`an30g;}Lxq_fK$5N+f57BZ2L_WEFW;j#Z;DyKZWi(M4J&t? z-yJ9GNSvNt-R>G4joIJd&*{JCLMj`NlcE0XSD$uY+-zyJr0rPc@vrNjsu72yaEg$x zQ&!erza`u=ZwU?nnA*bq{90DLc{BzBj51obI{uQY!beDKl#Nc$mI3&r096PKa`^4v zcYxz_`8CyDSR#5FU#d4r_OILWLs!?MLp;;jflVE|8P1Ys6>C%kOnJzrvkwR!)N50V z7oK58-rp@WO`Oq~4BfL=9W!IC0ouPM`A2ilDMLee#n2f9D7%A}wFY6v-i^s3YF^~h zR$%1++AIIWMxGZF{;{e4U83MWmWB0qb)`=lIAn6e(LumzfWIM{jJ!NJMeP%<`{e32 zu*6;bsDgaEu#iGnod!aI=pKU{G(juo?fA}~CHO~qgoudq6V>^D_j0Q6hTZITO*wP= zUgk~S>jk17f8kCD$AZtCNZK{o&Kl<=DeVf68^`9(kX2o=$=dRUb}G-703Z}7`r$AJ zf*7J7`oA`R5oz^jv^iquV%!SByl}LLn$$dibLFO=IzK9Cp7l?P<2{U8W4BG2rCQ@J zvoFQpDnFV>Cw5qVnLZuC3w&;(Bnm>Z+L*!g3?3x{Ov#`v>2=H@UBt{OMRt z600>d%%29A)nc`YZ{^yrx|qV2z${P9g8ilMrElmPCZCHpeeg(l&K-ouh|@?kdWC-@ zo@^$Tm5^{lOVCI46Em$1D4M+?Jjbxu3=wMS79Shrha2Q4%<CV-##Y%g4^d06Kb(-(QZUH6`gMg` z49F4y0YGEx;~>1D1UWY3=>e0BR&30nP9`>UPn_jN`lKgc^}W;*6HT2K$!P}m$$9f} z(r0JVGI6zfY$)6L3tc(~N1&A2+gpH}1jyF=VjE|o=i45z<(@BDT3Uj586K9%aZo-R z$MmIRb?;)d8k;S;W&ODo(&k3{}@9(h7c^yz|DZ$w^y ze*+$If6u(1rp?ov zs}T%5^Dp%sPj5A5g+=J@5jTm0VGzDBoW8GVAva`7bQ6}>!{0S67N2w#TVgVMy(^6_ z@FeY_U23VZ2B30*$>0BHKECz7ho*Rq+T0lB_u)(d1|#E%j(Ake}Nks$}Hop z->zVHuT)Gq{bTijDE;o{MH&a^r3D%$pMhFXr;@^0(viJAqe$i3HBptA)29UiDR0By~~JopK0b z)?@zp&t_%2V8zkhM}Nq=?2Zh#Fia=hx$ZnY*f8)@)(5$94^pqm)ZQ2*0|utdZmoFK z4x8cGXLkV|LN!xG8^DdOurMVxR;^8K?C4kuo}_3LCy{;~b#DUz-<`1l5`|WT4?;3VQzFT?%3qxo0oGJCYF~U{pVGbX`FpJ1M(CQ>p$XuIp30FKkqFz z6Q&197oa3_bNL0AZDB8e5%h3#`}(+G(QTI-r2M%fW4YvkYLlYwS!y;}ws^Ri1|;pR z_;}96YrAo{`T%E)>h4o{OYbU zi<_tP4D#^NesCX0_cba9I2r?FT;aMW=&v$>(zi7t80L03s|>F#ynw`7x}$c2(c>(| z5O4uKj~RT}5GH}RU8_{KbFAK{K%@mcj6w>?eI1R32>pCH%y*85^flns$12YCZYH1X zlh%?;xx@Two*Jd!P-1>n#AA=!X)A+)+5HHLOk7gZ;v29=U)BY0h%x6 zO_8qia|V+Y%nvn$Njhv-;rIFr^{vUl+9?Y;&SwXWE`Z1o5f6HrFR3|gdQ#&XkeCjf zEL4nl&lSHD68P*iR>B{>U~Lo^#)N6}u5ge9(g=*W*ThWAP>UuUa5Ybul6HaHj3;($ z`-*0UiMl2|b+!y79z$2q{AT!^K1ibfT1$Zz8h0GHj2FVtzS~uHW9Bl$&kT-fTec<=n(=JbAEMqz4myxbc-l zcKO6rlL|*CdaYT8Aw28lKa9B{Y%p=xO;d|Y{Hq11i-v{{kOd_WStwuaNMU)IoPH&v zWtqN5nhrY7_|L@lO$As&CNsCrS&-Qtp>0y@<>%n?W15_-cbcdrShT>kYEW`1C@5}3 z*gyWM%fYozPG-(1%pM>NF}C|MVgHM<_YUX!-~Y#7N|GomL=h?!A%u|ZQZ^wwSq&pX zG9r7mq^yMOP+GR^-pN)dTZIrZGxEDVb>8Q5zW@EM>vf&$T-Q1AdOkg$_s9KyTlb*E zG1cEcxDn>>nSO!6PAV&d9^7Qn81Ab9W$Y;=E#OkK8 zcWyfIK~!RlbwVHfuPdb_{sX!Zc|s!(&;6Whf9Pua=B_%>6LcM_soEW-H^(cpJM=rU zNd|51cZbepbci1hcC8G*Yh{QQmB@!8-GF5dQ9Q3_WwJad11&1@Rs16Ngr}IAy~*R= zJ-GQ!)poDPi`c1!wuvr68H6)g|0|`tcaB;uVnvuw<;#auVmVb#!j8tnX%jDTcM|LhD9xFXPyr+crwdOJdTQQ zv=e{K6;0I1Y~v)y17l!n3O`)%!Ije`MYkvSK?n%x2}m#GY!HiarA4$BE-2P&y`p6* zGTl@kn8aI5NxokrSoANJf&^roJdK4Bev*ea$h*)c-?T8u_>q*YP2auu3T^W7j*I49 zwqN+D8AI9ZjQ2)Fmd!8gGuCtun8Ij@GQvp?CD?=dn6SO9PR%Cke;(&aY4`!b(tPhz z{t>nJB$m=veg6D8B;z>u)%T5s2j((H*AJ3#(n4&f&JDH^nHrFj6Z%Xyb9GP z-^_z_CUh*&8G-!0vR5oe2d<>46%rl^CcH=TTJw03z{&}HFTWTYh=!KvN>Gict6u6g z@X;28+s+<(|Lp;pP=5gn#pb>Z^148LLEo+U4#~6Z@;q2Mz;3C8+vmC7u=GL7?fRNB%kdXj>tuHT06cS(}ugJ7s-e%Hq zIpXMk#kzrk#3WM67-WQ zQ{HY>A336@a&I`@DHKmbzF}DPpT`oLImz*dBe>1Kz1=rfkvB7~_OTDxC<6|0v#i)` z&lcYOo9%A`1HdvOFcO9{a0}2oUiR8tB?Kh`Lvl;UP1>E(veeNu$p$NICtAZGcz-;0 zb$rqXPRBJy-?#n=CejSzl$S%b-}d$*C@96ei6{Z>Z>$zwt z`@wa8znC6#GZ0?Lv>KO|>)zwxKajK#w@MjWvqo`)WWMKQUrp+%wMbL8&@oNDSE;{; z?Qp?j(kVZ53yqHid{ZV%zdz#5s@2x#?z}^B8i_eyerj=_q=zsAT|W{HP%qH4ew`mTiv=vvgnFO-YeA zH&UGGpeiWoovR_TIp^aA6kb4^AV)z!(vlAhG`5D zo3)1k=TTfVmH@8JSr|I0a=REiG3n_^spd%Mu=YABTvgJdr6lKnBtGrljf|G$69KWu z0*MMlOJ4ChI2J+~_{RaXfP{J7D(n&QoMk+Hb3R?Bxim>hOYv|Fd40a;~otf6fkiOeJNl07rn!L_&Y-M1(xL2Qk*{>ZX zGYON`#2PU0!i4)4;2)^(jU+f31`(9%M^l>SLWR~rVBiyWz?Kq`mQ_^>$Z=ypmd_)) zV}!~-%j>?5!$aQs{BYb++}Exy!ymSjT;Ufjqx_`>#EaPE8~Xb}1g&g2FS%LX#b3UI zmTImyrIsf1qVHQJ>TP8-sjcEnE}K&y>7zqTU^ zVGBCxVC6~3bcSZ%eD<%+unZLu6=C?f58^h5YxPD`-^I&!3YJW5C|mBCl-JV(_Pc`M z)k?FqIkO=*?_oD0t%eZkUUs<7_Q-q5j(CvP1(_vdCXuC~&N= zgy$(zmgFI(24$(OE&yYJ@IkDG-Uv^q#}VB^X1%;_Kc^n-Aak1dEY@9gHCvFJRq)B{ z-QraCuil?|L+y6WnW=?1KKAt_4{GA`&0Uy-FIy!f3v468{FcGB5^XD@D z7qp*5z)s(viM~N*pK0c4MD0FgI`2eAJP3^``^^K-mOw}gn}W3a)LZdP`9PHj4o+w_ zc=sfzBi`{L*INzmUkv(BqGpjuXe`wtQ;(ssoB#OLP&VlcM^nxGSxJ-u*0fX&5a(sW>(v^rDgN%VmVW*2qYfiH-Sk?{k6ODy{6S0my$>A}{_FQX zu%7qzO)Fro*hoS!MI|(_w~#7L2HHWS;U0EyhM`)3G$a!J=C1~}GBx%EM3vQ^Sd%FT^QfqyN;|iG!3>pVZBkBU9Nj48yaO$C5d*7-IJZ) zw5o){dAPE9d;3qgb6bCl_pZ$CqphR4F{IwN@B43Z5=D{e=6VXb;0?z&H@!J?@-X0j zclS>1YuZGNN7w&!z>E$ED;hhJlwsaaZ6ES|#HRZS4LbIEEdeMXh!_w6Ex?$kz6tv5 zG+SzY@WF>;*Dgh5My+{&kFl8=)$n1(g;oJ1Lf$NpWgryVnNg!x6`YtBp6=nzO^>^u z$jZZus|2P&86fYzGF=Dr>!meQar1{;mIDCp>mJyo)+Qt?d&^zgdr-!EY$jzSI_Q)X zWZ`hlVD=@f4K}Qs7Nop`m8TUry4CX<1v$w#Z#Jv=^Jk4l-i0`CEG;LmR-=>l*ynns zo3x)tHn^cdL!rZvHrc{+WtG-{`h~3L4`E3nAi%X2#BQN+AbMxedVg^w2u3Wa2BgEFL_rA(upZYDdRBs z@PQgaXLYZ!Bp&(1zDb@|;d3Jk=Q6cc@h)`p)LEfEMtwrlP=LJQZoMZ?Jm%H#MR z^XXF90c0dG>Nrt&^d{8NaM3t2YKe%7w*OjA^qM!6ULJoD-Ej;{t3o)PwwtC*ei>-Oh1(B-sxb3ebaHNd@lii@GFN19{G-2={`Q;v{kQT| zc{S!OINe!FT3<|VjI2#Sk_$f5(;Zyy)NSWZ-hBp(8-UKpA^t&RNx$-P(%7`o<}sZZ zTcWJS8>( zk>dHs;+lpL&?ulK4hf>oC=KI+i0JH#+fOFtqdQ2L=s3f}OX^0g=NJ#L8qkrGg(y}Y zZp0Lb@2|y37-jHlOghxs&M8z&YPjr^87>V{J}em)=YJTmfVlXuiwN&F0?)2@S$g|P zlmaPfT? z`uVoDM8#K2RFo~W+XSWauiW979+I#W-&F_Cz+3Hoh-?C&0n5tjsuxzm!sZ)h=LE_T zY2?El`*742QJJs$8>u>P9A3ysTHNl|9l~g4`Q$xLaUx91$*Dy*dy|}yg6lGkpkh0I z4u*+nD}oaR4jviVp&_sKX4#{g*WJnK`EapgAj`Mt9-d-}Tbd9lfDXh2GxhbJ1{d=F z=Mt1X6jF8q47dJ@6h7dF!lityHEc!8KYR?1_}k7tYHM($KG6PhbA5iWatb~LjsqKR zq&O2XQtf=)SsJwDr)u~vDF39k4;>j%(bc=!@LN{nBTuMok~~eSRio5NE`MT?C8D07 z`nD~qAK&ym*{HzqCF~49ISErkLc%Mx7B}a_iL^L60=D1IF70?}p<(;SJFzYA4S4Dg zV~oTOcqlRST}#R3-cC(DOFf;Il?5pVf=LmA7-)ePP&{0TkC7)yKEext%lZ&>DdfjJ zt1eN2BmM&%f2M+XPKWj62hSZ8o%_d7R4oOsX-9vg)SbbD$gx8a{`Kou%nq9+!we_O zz8y+((5@3ZqkBt2;PEozsng9ErGB$>liP)&F(u5jfC{l9_f)2}>w|y3U;E=PjsPsk zW-z3uJ?E_w!o2?O+m-L~VTYdang-CyGv}G?$MJXme0BJb>&PD``hCFoC?VKKg2!5( zGPxx~>8(?J<)`h*>f2dwKz! z1}Gr_sEO4WU0q$dVbK;?+?m5rHGX za1?>*gJFVx7A!zlP^IfP)Ek^Hw37d!w8EnH{S3e!ktyk0aP!xTsB~Oyd+F}x#*74D zNTPOaGpderEgfIleV|<;EUvMfHxzfsd4xKm_3*9Oy$}V0roz8PoG&P&S1upCnJ#n- zjb56SjERpQUmARo=+7E$C3Q&Y=S!_A!`U!R1eg<&2uM%-?@5{yoF@(m!Vjn?L92 z$SNmYZ9*#BC;?Qx$q(g-LhEhWf^>?)sX9SDJgK|%!+OE z#Se6kbd-# zw+DOw2W~c*Gi}l6ST0G9(xOTjzNXKz>5X6C2mdYWsgnEek&y_T+N-ebOM#TVdDQHV zbqEIyZ$7_~HXHSHZCLqCLPB2<|1ce;F*)af`T4SNM|bogmg?dW8-VP`+COqYvJOk~ z#aQcDCbmasi*kYOo=tY>E^)G8CaOTU_M5=!-IN)PL*l8HhXZ&u9?cFaWMKNF%Xk_Lo<9y$W{+I*!!9ppy)I(L- zxic|&Hq<+TTS6%>Al6zm&5Bi2d0s5E%5RUs0rgiRWtzMDruaxFe~j?zrKSB5V8Ptow79tD z=lRD6&k5NG%dQnk|Ej#n#RA4qW~ZB)ZQ<5;yh!|MH4#H$oZT#bx7d~yotW*XDS_aM zCI_O22%VXIp=oKeW3N0VQet7MCPZ-%#p{n#?W&s+n_l+M?0PkK7j&(A%jt*f)*ldI z@qAG&CZ5Z*C7Oh@mdv9VZe=#fxyTqA$Id^9AZn}OQ0vrlsUnA>6dnasUx z#-;IiKNmb&)aNC)M@Jl|JrsX!qSSTr`pzYzaRJlo%9}Tkp@lTiE{3jFEo?np?=b0M zT1{Z^(CcgTrA-Mn0(4@K()|0~UAVKV4|aZBK%0Sb3~4ErHg$ZYI3IXpAoZlVl&EqX zdIy9wQ2O=nOzA%}byl&VqJj(^f{=uSyY)$rK#P&VXY~ZNp{t7&fqE+h39Lh6--3!q z=qc|!9)r4)lXQSYLCuh#Bg4<8%8<^eTO)^{XfnO_ZHMlG9e81X|4bUYpQQmN9UfCqYkAju#`qs?PPd>Gi|VbM zS=pkamBRI5e!h+OLHW&dTAMkK`c(|yZkps~*GDI8{CunG=r-L9$%LCXWmS@Z0w4{9YT*eUoC29-Wj#H3!alIuyxz@DtiyN#@0XE_^n#CcB0kOb&A{b<42hSv> zO&^7JafmS4E}Z`y_D8B=s%^sq3j@UzH-S8{Sb-zaGd=&EZgDb>hfCf6bli?Zwx`bZ zisO{oTOodlpS-bW+EgVZ76o^26cW+o;w8fcA!4eap0`|iB6Z+kv zjlsz$8vOYPutr06E8%D|%}e}!?7!$8w!xT#6&KrnZ)Q+G$r`Q7sLSVbj3v*X54gN2 zqN>l&4GRSKqHs87yz^PtK|7OMl^VyS{CP7zMw8hB#l2}&#nann(CBvM%CCW8N;LJz z-rbi2^|zrkU-$T!l2gZq_iE2Bb@KCUC;8i&cQwj1FPUUWMp&nfC|^k94aJb*zxO1} zB>r|z2b;M5Hby>~y1#T^N~cO;t{Rj;q_TD<@npa>@>H=IeII%^V&Nb%6Hrw;q!mBc zILJp>Fz%uxcSMiqU&X_G+q*_hiw7hs9&VC(n1#q(hSPSH)WS6l;HD%N@(s-JAF3E- z%~uOiLlyGP8zxGK4;hsRY3usTZ7$?4Uy?LrC~eO|_AMMYFNO*UXAkrJ+Lp83gd&u}3_g(iSXEXuhhr=91`#QTNN zHpQ*-l$a4Qo6?6=$LzRJ;&0|33o|fWY8PJpIE9MhsL&pg%uDc>s4H-@@=GNom_N`Q z`a0Mucuwc}qCoqFwHwF#53>aN@(({RUrT45J?{WKTfz(1@?xeKmTwJNLPIWyeMlUW&&Se_*h1C$M zBEO2qg508zjYHuCApOzYmi(yLt~%z(jKNg4tMqdK#oNL!H z=^}GhcuD&$Skrb}%^-MjsvC$?<7W49Hmb|&cpw+NEmG>R9iu;ARK4J%1LS|HjZ-y3 zRhqNl_3>;9mMwBgM80^U)@4GI)|Vu&=FoDrN9@if?>ZJs00NK-(mLLmb~9>WB*n$xt&Zm?cC%YH%n)uZ^Zs=&5fVjILkezgo8?39J0pjr@BjGp zFz^Ue;D>IWwR}F=vB@YefE5$e%a`PlLsJM>aS{9cVS5X&_sB+4^*Px951Fv4sx_)W zvcc&@Wv=P4%e%v@Qb?lC19@XRo{hgPR3uetwj1cmAUgoUq3#_HPbu1Xdz($B=ZUXB zJ>y*8z(ThhyLs-MFwi!`c?GI7Wf?Rv$h4dbHr!cRMf;PL+eABpl^KU0yOC;~$mK>( zC83sMq#v4^q9qXc%1U{&(~6Rj3OQ$-i*^biEdf2)mm^e1{He>;-LKzg-&=ne?+=c} z@$pL7Qn3*HhapHxJGxhpUCfj@u86G45v0@6Up)=3HCIg>Vp|=J zOYFqHwcgT9N_(RzVqt?uqQd-Hilq>BTRV$Mw3SF@-g?vd@M9)xo5-?cWmDvg!)7F8 z?U;Bhu^rEF6#KJ(@uWz&N#^HBA7)C%s<%Ri^ZdC*v6iDC_XS3)&7s<)`an8r(}{nQ z)7VxSl;}{`F&wyKJ07&Zmt?%N7dbD#%RNS$47PVo4x3Tr3W5~}ncq775tVY=5N*{E zaMw3?%vzNIykXOLoOPjZNsIW_(9PJ4Sbt1HOdkB^7zX3=GP5p(R^#skYyylveaLL; ziSg***mKWq>1Oy=!)DyLYn)v!=6|gVdB__It&_PS6A=XCpFV%kJ~Tr(+z*>w^$$?a z5#G7zXKxnn2W@r6nwEP=V=v5Ido(_7hH?yjSD|q&ky-U|+c>>s{I0g~k&T3MWo^sd zSy_m^GS2vfpPkSHA>?GFeTTO}*tN*=`NJ62FCT8f%h7hLaT39@SJ{mFl>kA1nnrj4$+b^2DIwdQ^-9y@OA-;k}Ue40rEail84m@vnB^$w` zIL8Z<$2&@lkM?C6aUtfPJ|ZKD^jMyzW*>`HR*%qeSHEOl5LwXHT`K+HiDVJ#je!HlPn6d8eTlghHqiAF5z#Q9t9aV7Z&LF*Z0;oL zzkB!ah&wsSq`Hs{+NEtlblZZEvAnJSNsR4(&(kL&?si&A09E80{1fnf)gaK6+e?qn z^Kg-T{0 ziL&wQ+To?2fk6s_jt8_W?9mjAC@IoZO;HBfXlAEkcE6E!()=Nb++uuuyW5H4j(h`t zBF7g_Rd71aR#`aeBKr$?2dBl8beHxU9HB7D+q3JDijaFv+8Gb#H@zI`F(H_$(Afb? zj8+wbxa7_W|3l4u?Wa3AMb&0LT(e=J0HKA?zpzDG4P-|!{*g`()Sei18H(=^kT=ZK zJt{(<*ve5bsV_fz3SuN$`P(p^78QMV)xFVoaO3Xuj#aVuNj5JGjX;?v=_l(#0Jv@Y z+J;x354(F)a&u3PgmjHvTkc?K5S5jk{LySGODX0md~}IW2w;VQ9PNH#iN!NQ)CtE# zwR8TEr?#4H=oc29eR?q9sdZSE^?md3-O~W|X~I+v?3vpZyY!X1D7RRicLbTch}XgA zLkAq-y01^Ax2^Y>&)dF_*dqrWmXBM#xSY}zS&)^*)4G$|d8l@`lkB0x%XCD2 zl1*5seea5G+oY*j$s*xTEPu|fBOX@|DzynTozh}7+3j?CbT@jMf(?R{VtCTvgoFSY zM%n2Cnaq94H3wCP|89mtAs|)~{dqj2psQTGi=CZ#_NU!TQi4U&A9Ja!X`>VNkO??$ zzLbQuK&7R!@?6uhGEY6N0%&R8UJMINxn5^xc@))>l{J5Hzi67sUm%xOUL7T9Huy*IiDd)TP8Ldehf8y+WuoCK$>liK~Lz&ogBxwH$RPevB0D*3)Vujo6-< zxL`Xne5YxRDKc=H+t=E>U!e3tu4R05WAlj8^O6#3EFBLP_uC`Uc}4YL!N5+{B$5hW zEW)>F206|?W=rwDtZIXmyu7@HC#tkf1h8l#RmLVDpN*)!v-vTPl3KzD1?Jn90*hdAz`7{&LCL%XO4&AOz4ZW9 zfV`cuND@FWaH3j_PXB|)+lKtw*dKdsZ_bjUuw&islcU$WZ~s9A4Y)HvL5QmXG1qxd zE^hdm?t%NcKDXjV6f=ZB#3vk~vfm`Ae9A5s2Qa7ZD}P7#ZFk;{-XX&;M`;1s;y%?c z$K1Lzo`H3RK4LKFn1tLOp@7WHTarE0edk!{R8qixR-MrOe`aCi#6_YZ)G0~g+FRo= zTP&;g#+-!$=M6q^SCJwcUkzNxR6<}RhlpnX?mtgzUUr7?Ue;oSYLW~Pk7`_JX|U7T zjIO>u@y)|Tujj&?$B}*P6}LGC#(e(bN(e(rm|&&VLu@M#?|U!-OdxdH=wKi#us?H~EvrSmy9RE<&fpeSod z`d+2e&1_;HW3^=_XUmMcC%UwNVJJiE;)g9TBYI6ul4=_AL z6GywDncqlqG~N*u|Rz%*#1HAeh^#u0pD#OkQ z-<-Z_-j#Jxf7G8q3l=0V!=^aakr#&$tgFuAp4YFl0lAZIGHOjL(yM$RKT3h`SIeEs z0i?|LIO$&mwL_E!@|e4OUlb+ejseeNx+PG|6*?*s*Gh@r`?AHthQ}RzT>tB-LB!ZK zVVN|Yv!gG*;K;7H*bz;;+F2R6>9H^Exuqi}Sn&hr&G%($;Vd?W(~)83?u#~;W$oAP zLh{oG>)fh*Q|QZ-cd(@R!&$^r&6X#l!n=ys6P)?&wh#W2kyZWukN54!Y}w=WzlRuC zugl%ElSVBo2dk2jwDHO5AC&vDz5bNRr&@O{&mIoA4+!-R2Gu_)ryG7!C z;c>Xa-dC-B)8(e}tsH2{)9k4H%rhmfo_S)1$@A3$SLz#g=Q)L>c+g`1QM&;zGvL|~ zA2@tXL&>DG!cHE0imS1d6d2MNZWmU^X>1YO#avfiU4A@scDL?<;?AB@%17nFbsUKb zd+F_#cIJg><(zwfVg?PeLZp%e#Ugmt7y(s|n=-MN)LJgS#?*Nj{)4p=NS ziu@}*BjQxy#558AoorpHl4*+{WJbCIl4oTy-G5MF{Asu*p*-(nBW%} z_c&97F;wc=b{wJ49c8nwOjN6aj^j9`qy^WHk0XsU$NvgA8k4hrWU^@|fGCFt z#qZ7?x_Y;r=cYNc?kqrS3Mo#Y;rl(u37aSKW8W26b>-(#Zgw}BUey1{Z=awgt|1Ma*a<~lp>WL)i zren+eAmJevdgS@*B%vU59}MC7Ko29?!0M!rY}xMyhrz$8X~!P<{JBPCVMb5VE72vc zj@wKlj4>vUSb<*g0K2$X7VBh{F(4TAEu8@`;jvaoOYoh^Z1?ud(LUx?t=njY0Os}G z@6`!XZXX=wL$6~n1rTRoWaMmeAU45WshY6lAGm*^p5Yy2Yu>byE6tm!YWU>BtqgT; z3)_m){e_-G3mN)sxqf_#NIKIzH}SpmvMaCCyuM$^#>;d@|@?9d$NF5Tb_J`nNATeW8Og8yHsU{7EE^} zl?jpRxSKaOVq%TiVa}=+cC+GfRgMGA0TzjyZSr#EZS}#iDnOe)ygVH_MCjV^`$|xw zDM&A255~qXJzXs=Q{sA(Uxv)M z?G5;K+jV{68y+CgKoDFR{5oFhAGbfH=Wz?~A}DGc$+_;l^W6FT+zIM%?x3-~s{5!H zUtd2tiG361+eNZBvTQWmgSMg<%03y8VHqWyC1@cL=mtRO%aSsrZ;$08Uc{eyRNN8& zO|43Znvh4uzZorx#O=JfILKUX>%5h()Iaww@8y<=keX`&tbdhWAPJG}LS$g&#!bBp zP52{ij~EWDlraqVTbRev=HxcDF)1iFpqD@v%%$aqMX_Ji+qr@`U<4^J$*@y?K_%o* zi+=pYhH$Miaau})^_U@BA8@po#FnpY8`;r)cr(HvAX;sWnT$gg=aD$EHH79M{P6G^ zlM-EC$uFu$*S_y0y+uAA@e-6E=h?1#b6T)QS@-#mJ#zzWL;iT?D$PBmrSZqvCe9QcoA!Ijvv>z;3MpHqA*KHsd*X8U@YMX9^4+Lb zI9&zpdx`>X`K$~TxJ|O-5A|C|_WIZ-ft6OlReFLQ$Gg|$^JiwZ*O~8+!8dJBEFA&3 zptg3|$A<&Q4GL-jGGqnGBb1MdX}fL=)S7~}28MSI2xCFtFl>!LkBR#OBS>K(A^3Ft zc6|kd!N*pu_0w;!G&~0x z)cTeo8~yTYfv^gA&Zcl&&W?!cg)=GiqV0a{Gx|U2SeRjA;#83%(YIsFKC!p;?(Oe$ ze;#eNtmw<$@(`$wkx>;A@sTa(8xj(dd*}+TC|>_7a9ZGoxQ;jWfCZQ~-cVsjc%-?D z%WSoJkqLBJ0j%;aQL26Zdsj))vbcz~oqj^S_b0C$)FmIIS>!+>QlqK)Fo^0E&xag){29Ph~HePCAkt-hS*(ZYg(*RBJ5TsUy`+x`yT zAMX5R2WZ0fo!o$xs}9HkBbH~;<>?AJQm|?b?Xo|*FdTxH6#q!`bM4QbvvRbW*lP~K z6R3H@t^jDtOIS|igMTStzrTE0#TEi9<4HsWTv2I5L#b%z_;;H%EMFT*J+D~XpG!5y*@#q%>?&`jhUi8aLJ&AV{MiDdUPbdTI z&Cd;P^kSC$Dhk${9;P?h$mb>dH3vLhP2tu z>xc#x(LucllJMEH=Jp$(npmtW?bDx^%n+eUMBu~KA8#&x5lE^%@dk?$heK`i8D#an zj^>!b1Nu?l456+gp>oa#Tt8hdHYb`=umvjE%VN-{GvSL`4=31 z{5G&Z!E?)5U+$Z&zc!@c8``=Rk{L<<`m^N5{Ug^*Gj*W?##jw;#%b;Wz(cm8p$7bc z$8YqGmENE_-}GpNQjt#P+b=59Hwzu?;7?uM;FpPc< zwgJyCmS{n{74m7uNN%%ue_BSQca^|kFh|NgzJ!4llar%R{{ggt))I|5GHs7RLD|?C z*{0@9F0RtfqIU_}d%=VDSJ|X=5&@k-DEn7!&mLpnc5n&F3ABEY)e$REvEmgr;bHnC z@u8GBXm-^l{J7=}(nOU1*r}3}d%el^K5ww?()}mE#^o z=q^~&7U*<)@FQVG-*a+JkYztfYh~Mk`wx)KHtA~-zuVGwM{;k45H)tUAh0K3tICFb zoa9I>O6L8$$MWB1fhNN90*6V*=x=9AU?iM(cRFwL;Lt>_guVGT?lfj9B zMo#w%?a@CVL>X*r(Y%TG!yAc8>GDKnY;C=Jag#`S>=uOUKriz&H+OI@&qoG=(>ArE z;yG#QtdAIRT%-PNY<}cEB+e38fss#Hbcq~VFY*m-!F7EjZGY&6K1^S)*}XbjM0=XD z;`8Ta&d{~pq^&2Hj_n=Y{4nNs*1^)qv(u_TAO1EVyF_k$-#?70zBc^C-ods$O`FAO z$saqq@4BAhXUe=IRI>pSqe=SzLX9?uR+cTMy?2Q#ws%@Fb{`0o_!ejqZr_?qMEC(G zg46(3xh50CZ2iTC(*-SSbwXQzl3PA#zc}>SZcy!o$*l4;qW?)iMRdHp7;#_OeYk;L z9qozA<)YL<*wABR?=AdnNQz|xm=RgccbAR4_pLDDFxC9U_aB@Z*#wj*D*#x2`t)gimpezS%D{s$+m&q~ zmetl0C*h=27tiJ%7xI+#E*UBw$q5zro_+nRZJR{ZvjWfGBAs2oHxi-=JVbE%ZlE4o zdC_^1Yxrbr56{=Aj)XdUjmQ5({~=u^R2)D&=)dUqx}ZkD+d?n`d=@Z0*fS_C zA2<+n_r%_*{)&y1e$C%LU5(@*(4^@f*?BO>cK%#bpd^^eqjG}G?5ubN>9uJNY2JKHBmxs~vBiohi`?9vw7 z8g{q^p*cAUufh_5PzhW2PyU9xeir*L|1@B|v~t&M&68T*=9 zVqkdu6g{0rq=ckSet79Ip*2L2VlK-+Wt4RPSNDJ9HT{yZjDlt=$QxBn1^6c{E9)?( z&V(b~4uZ88-COzVp_T4aYi^=z{$D=9%niEELlh*k_wvw~)kM#qe{?*k&=4T>Kb!@BnPF^=`-No44=A*;7 z3B5NEf=IJluG7ge&;E-LASr-oY>k-xBD6%Qs~+Cb3Pe&!tkS#DX!o|T;Mg@u-D?r>Uq~WkrP#L?C|dp!y`N5B67Bp5AweC?tz4k z7(>to$M$0iC0)I}%109dbH~s|2r52dEf|eo;6|p8noc5b9bpRKhK^_530>f|>{@q+ zw0fZ3g$*P*#QYd5uyzbYN$+$>p3bOgX_{g6NlTCZ|4@nZa|pv@sV#gjWh%mO2#4DK zdnj8kdTwQEJtk9+HWJ%E7{PGGq@$rpOt~LToKv`KHJkVru?Aa+TkHQI@P^@z_%NLV zT+efJW-6tRD5@wWq&>+}Qp!VHT6|iwbG0s2Za*N&s z`GNM%t(@eLHGo2ad_%qoEG~ht2~2(Y=KfU^;&a&C-n~0vH0nzcnjZul-jCnmhE)m+ z+cFPjkVEIk@#tyQANTH<)(TNSav$8{UuE$n(kuG-3@`O`>a<&z+H~#2zrF|0L({bx zM>dh}zwb*jZQ4Wo&|~r1COH4GN^jwY#md#PB{ImPnFN_d$8O3_=DxS9z%v7FXW_j?W2YCbPCd6yl7+E4KG z9}nJkd~<)!CSJ(WN^1q}Bm(K2L|JQ)0YVaGhC#a`4{U= z59(aBx_{~Er30T8vki*xcd|*{bvwhR&tHG=T1W0H{=5T2cP%?UKkr~Q{M5N)j2;n; zC#E*hD~g-0_R316r_1^6J#nNtqV~6LPwdXdea)n8>WMQl<$VirO+a%NJclDUS z`w;6&kdQdO0QZYWO3Ier7<+l#nT|O`9K{dpQ{C6EPwpI1I?LMKcSJ16*SBe?zu!#Z zuIs>pEP3533F3T2#znkmbN<|ZwVEwG;`*DxC^g-@erwdNhVVu*(O_|4ilES@+@)!V z%7^vi+DWyh@rTQ&q#a`Jlh^EqYV!*B(?dCvR!M;+^VzR?C0p>6AfJFY)YYu^mDWcp z7n38^bkQ^$B>a=7B&44Lak4-xeO}qp0sL}-)zd72HO@-os5X+4|#*wuk&mHj}2N1{45{1%|S{c=uJYDQ;-U z2PRB}m2X;Ioh*Ny7v3c9`_DfbM^-Mm%zjP$J|7epDz3h3qdlA&2?1q&Jd3N|_+UZS zPT6~Vw}6C(_!Kw^0HL7*k--MNVJ$tq%Zb)`zi6lY8nn zIU186>nr%y)Iz16ks%y*A3-1Bhl2O9(QwJ%@ww~cHt2U2YUdY**R@qD>Cs>SI!)IUs8D2s?6TCkwHi=aje$b=7e#Mup%j=XL;$SKc+TxF`WNuaU?0c-~&@grp9f z_O()&G&W-Nn+l5F`hWNwWy)k!ukrp~cMKuA_X>BO9guT6Y?{Z6FMpJ+ujq_U47;41=!byZnTni(?qe!86`fg8k#mfVC)hGF*z<~ zBgu4<5luN3Fh)Wtfw3Z0gpDdacEVL^uTuTk9P7tZXXL6wF(?z<1ECDBIfws@zD92# zhizHN?ra*a8mhbH5I=71rYlj`IoM-R!r#R+k>EzkE>Cx{u|&VLjlG+%gj3 zd51TGu#H~EOru9Y9cnY$Bz75HT9xdSI2$H`tqQ-B#404O0-_4%XUD064@d&j8ac|&v< zwbRqWP{{J|$^E$!+Gq2#e+1+#EK&T&df)J1d1O&W} z-R`PgvXfz(EbO{U=x%a0P&CZ|DnP23`_k-asvh;r zvPBM8L*$o!GyM|Ia+N=$)7{V*^{ShC7!{l-0cVGb4-NO@LA9;K-^2C8bT`6_=tCfB zH1}NDyZ3m=d*c6JYs4iH32boLvlH1s#;p; z5U7~kVoTVJmcwWpk_DnU~;myW-dkZ$ba2Gjp zgreVby4KU9cWh8iFn9B~-_rR`EbU=#bgK@=`X%uXyT7s%Hpsv0aYDssmz|HP+j%9o zbVpft${yx3QIp#5lke+RS>1IK7}T*u$k~ZKiu^rMN9oeHiha*Y15tqYr28tHO8Pmb zI-J2oEc574T(<4!owBS?A*WVe>VZxu!glzi4=#C`iyL-Wb!JPn+v9$L}=g(6=DEaG; z&d=sT@(qy!j4dDa)Xr20eMsf;%fa4``T6gn)E*cD2+CLG~8Xu(oT>+oUgm`CZh zN5eGSpRxN6#U(@$ntWo>s=i>BQ3#c|e(y-pwrH!}oV_oHoF%Qnly9~M|<$bt+@5VEcWn?T)=-9xGPDBDilXD7w_PE?v4=x>t5 zT`Qwrw?v*S)#obG(^G;G0HR1>5^%6VhitiEH7ANE2M0Q^&OfWmCLgWO@#KWs=X+9~ z{Jfz)5IdDv2dMmRAk7RsA%ZlFiX8Ke9v4#1WR5^M1!XvpFCwi4cFdt6OW>odaaXjj z4t-LS95^~iQ|DAW{3E)-I`x-LXZK2R^VJ(_ zY?(Oz{{((AK6w}OX2py&;EQ$rrWAK?ftR#Is$b&p@7Mb!w2K&)RrUv5p~~yH`0z&& z9cnIO4RAFt+tIn7&H2Qz;XVVWQynxnh|xxrp{5c2^?0sr1~i!&g(Hg3&YXhv&d@e? z!*M0ir5=`@zXdt#sbEI{%n0P^8;@$bkzM@aPEekRE+y7Q*xTn0?45OyqHdaz01}5$ z5mv{bD%1l8rud0L>FMt;Z)_}3D8UxW!fsjr+>N`KZ{}u8NUzA!MR;?_C#eiCoq1Rj zcY1%@ZI-s2q3V!6BXRkxkGu&rk@IYcO>X;JG^4HHRm17C-V>bLt8+&dX^VQ6)qFcv zUe1NIQvbbRas77Mlzsz#*R<5yhP*GCIHfRu0-FFA_NpN$P|-yyX?xM`m-KRFZx-?@ zgg6&hg-414wMVz_p%m%-vK4M4CTLX0PISL+BM{b9ZDL{7`}gk)EvrlT#Cd#i@^SB% zzOO2$r>Dquhhn-mXK34Tg{_Rfoa`$T@?Z65u0q%x~cuY{27> zQVOaiix2J;C`T^VaXFqT;j~QJF!DanrB#U!Xt)~%+_?$EER zVeUgwYL^6$8zqic{4?}rXuAJXjuFje$*e5ag_!^88ZLbxjBa!@bygCY$$=j2n(CHo z@1AI?5A070WZAUU53w2nfjU@s(`Ed|q?6MevP#RDB>++=E+a)$N_@UnH_Z*lHnDM4 zUM<$IzGl3?J^m7-8CjDLTdFpRnmf6?oWt#>^qWh*=dX6V7DI*#1O0>)c(}bzk{>-tX7z`Fu5@tiu;+r7q+IP7(eM%roZ<41Roeej(yvp^X86rymyr zUY#e(f@~qYEWu^(gI_gcb^5*7V)m?WwtPU*cX@e(J?J{s{c*A;$}bB>qgzA;?@AY@ zq&X6sl5ukYIl2vKTc34Ad5peemy3{m^zn}>yuW|fL~;I`Z-gr@?0+}%;&N!0x)wV{ z^+L}zr#8n;kFGAZJMY3}3PB4B!RiaHwBBn~?Q-Ve@T88@3mDkgj@{AMcfZN?-^0_c z@AJIMg>83O1;$QWHV+VyrXnJ%CrE@@>&+VLv(NV=z-S5z?)Gc$M1`9_14~^{Vi#P; z#)5-J`R|1IHw&eg`?u;7ZT<9HxS+!{LCN#@VR`A$($E8WC`jwc<{5(4I z`1BG&>+MLUZ<&y&_o(e#q;76m%gX8Myq4m_XsT5_dZ z+*Ba);L=6gmQn>ZIce@gE<9BsV`+rgE|T5MdHgIUN5C|Xv`WlDR(va3e5d)vD*0&i zW&9yA+`y4^!Rq5XF%U9dtR5cKq-uLMeP|ol==wTOfBuH%5j*02=8kAYIs7ox70_8* zUh6H+4bIT%_s>P1C(GAa&2tMza6JeU#TINK5L|XgQMO}C1lmeBu-_mWmpa0Qkq+{I z%RvjxPbd#?-Wzwtr#TrH*}Plzs1)iCJHG5BYopU2R+!lV;}v8)hv|*=U%e2VC982g zotJK1B@BSlCo602upKE<0seKsaeBFETCmuGTGh_$8k}*Z9V~(Fc*K`futl`e>G%mt zWPhCI2AL{{5LJmqHtcf3n1OJQumvINm8il#W2FMu^9UhD@81 z?&2_MYz$S=24MI+RrC~jNr^(Il_c@6TUl*sO;Rjz%5Q59INGHK-kMfboC3}_S71lf zEGhR@LuB_&3>PIVSfz2#RfTdYj72 z*S&J@e zvcIOLiIyFt%OGpy-(YFeZFZ5VM3l;Vi}p#B^wN=>j^w4M_oCQeId?Gn@WygiYOz%lR3u9Yz&AmS-`=4lKWS!BsaF!YF0XWr8s?L#N8ebfyG@%Idn> z$x|iX>*WXra^g%6r0BcXIwt3Mn!D)$-VrPU5Pqhfu{@r}n`odFI4Ur{tNg~}g!=V1 z_2h&%cNv+*QzxgUsvl@iU!F5mYtd9a6#jI0#&vpW#*Ex|GB~Gl4@X0pMh1_lMMFaa zR79%z=L((j9SwSm({xRwc3X6F>*zk8SZ=M}N3pzGje=fH5)mdpwX#(nB=!^46UNph zw+5yfkQ1c+PRo-j(A7-pfkd$MBMp*GT3S~k8u=k)+?!W%J+~oB_w>xD$S7SWULu@9 z451BXfzqSz;%%AiSp8At)@o)tR2`7kIXJ87V=_@~dtmI0>}E_=3>Wei?^Va82>H84 zG>vdmk*aD-+-5+T0Nhn_3IQ;L9m_M>vjCaRmJ!lG)>jU_erbhzZP%D3yDV?=iVEnt zlNGUN8&*uzb2!`Ditmlmh^Ercskhne8w`^fj!Ki&uo2_A#f>6%#SILz1FB`KboRaE z8oN2MRDSqSjZobI>lMeB%moZS-KsAxq&Zi&a^z+iDk$=;dB|f zUiQTFND1qn^L`D(K0-mFYsL^ABcl^zU2mCV!x_8BH-;&b3H2Z71B`uF44Z9xdYn5; zZK}N~0dsQsy~#VGySLPN{3a{wQt~xL@9ekNw{=?4g*)9jC$c3ek0I{H$Az&;$!2Qq zCr<>~4UD@Gv-WCM{{H>@2vpi+5KYI$_$BwC0kD}&TM#-x z#Erdb{LCXz=%>61Gigvt!Zy2o<)O&XgR%_$I8g#00LG1ODY9j@%XPo%_`p@=UQW{p zZpnuoY2}msrjkfx`uh_ysad*v%h=I_lfpfYMUHT8{LPo48)|W;r*Tb8XO@giBPKYr z9v<_sci~#plIbD0lN7yen?gEtN4gM4B-eHL9e&G``o6$p{)xG>n@;ukj-=N-fC%I%;$pta#hx~ zisR+B)^8UV7h7u;BQy^+IN)(p!e%}Br38YEZ*ON@cI689le}N3$WJMc4wBAM@{4<~ zg0*2-xbvh%hvV#;e;~mJ03EsoHwvZiQ61Nd7Y9-~ub~`5@8jt>V*KL!VNMg)E#&I# zJl~T#tM_#Vw_cZx$GVo|1&VE!hqHBran4NqVLvBbqyVicL|M2Hpb#qLH#|Vw@nUPI zC~3%FJ0^fn3?*XjJ!C!`|XxEL}D{4zIT{26v9jNEfacn zG_uUcr`odE^>k9+OmtOgKH4%A;?^-@G2n+w-6~!Bz$m&5P?J7b!_FXjjaC zI=}Qzzv{M~*KGXK9)OFE&7vUZV@qLQ4-Zcoc zs%DH7fAcBcPFihDA}8^j&%RjKpt$Zpw4IUMf}FHsZQ+ZJcO}y}F?~P4OV+ga!~~pD zeixn*sO-S%s5G>-xjJ=inlhdIh8+@+&+^1H^$Dqu%O|H{JNk>BOVysA9evc^2aE!* zDmkgoi8|@%%dJnn@n=FHUE*C$%1qw+inyVK2^H(ghk#J#?5 z_97u@A&8YozSeN9i+PQgcx5YPh_|W}{-kUBG{*lOMV12A6i6C?qPW}olOFNFr@GpP z3!p_9w%ngTTk;wAS_MjXeE9GowsbNZapkCqT3^WmaG^Bn578Rh98kR7_Ml6>Ocv;N zMB`W37GIlv&qANEP6baC^dq0Hrpt=r1~nPd2SVGFX@ zaEYT7Lm)$X#iGT*K?ph^gr0is(LbB$zp(*s3X+n3cAmrRS2(8}lU6vq zevNtIJ{Vl|np(V#(;Xv{Yvjr0uH9B-h(wu*9md{!>=25k|dWOBat$nz1 z{m;1T+I-hc>i(P5e}5Lg{hpf;dPe96zkcPen$NKt-0VHQc_zktIYxq|ckr{K?4I0P zCJ+4?=i^b~aynBdcWtHi+8bOh^eE7I+V z`ZIg`4=)-7lq0jU?%O$H*1$7`j~1qlqk!mF!i88c1y+*jLv+cpt}5Fv%kJq%>&yG$$gKMpYWGat%+5T8 zCjm=Ky|%}!GLgG9#t@Dr{zA(B^9i|mWF>8Fx_XVL@l1$!Y~;jSS^@Thnx`>(}P zM`)faGOW}tdug0t%i}%1Xg@eX zP%aW&$KjxH8Q~(pW1uG^kVY87a{IX#i9Xu+7E}1mV08d;@O&klB%!x~AVPDGh4Vvx zgNUOdL%-IsYg_F|eQsqpSnSmvRQ()%c_+{h?Z`wXyXHh`)rv!pq%M!FC}qpZhMujx zP5iiPmzgDrepWbghF{Hgc};drFe{dJS!bFY0A529>tRQ)QJhCKp6wnna7Z-Q+nv7YdT{rv}~)_+aC8J{O`#EVOBc94+ZsrSA`{A1JZm2fTDRm}LC(Mt-A z{k^&^bu+}-DNbo>9_kS)jp4SD`VXv*U8OI^eD9;&NZzQ+#9li!P|{2G-KkfqTL>Q) zMiCNM=}joK#12Pwo8)U>#xSa#7796dnb7&5fAJ=&Rq=9sa%K*?PD_Au3wnMJ8G&Em z(S+w=uqGhdtN78sb&v}FxB6B@$ZNY1hA;pIfOd~oIPhU}cmUb>$iXDhSQjn9ciJ+rIQvjMLSYQkgV85tAE$T8I6`mHyTMlno{ax^ec%#K3^!@3*8`_o6kA- zha++{?6}*3`aWS&WaN>PZW^BdQBJST1cU^S7z{U%oQbhoZ?!8g%ZV(sv?wq@oP|^l z%wh}8hy9w>0mo;!@^QyB8LLsn z_EY_L~ca0#N+^5J);qo-dmqG7KQ^LIUX-~j1Y^9opLK}dt(hNeb69D@y;A^l6G zicAc)sPU2EsJg!8i{*?zm1It!w3g)I>OBzA$hRhKvwM3kIsT|d-q%QH!R7a54xA~y z90aPPUO^$rop$)$O zlwX__qN4zpsA@k|Hx@uDi5M%s%i8bX zg>FTtdH*d1!04qa5aA=zbku!ocbHr%scwe^rd-4+7~Z&OXCdqPz?JN=Qia2KWd`$1 znqkh+sg1c)bk*@yRaM0$8E1rUe2u*N%P?oOoa{=F^JhinJ-Lq`Y1rrOq;TZetftN3 zy(Q~!0yox6Tc*X6ci7UcIm#)1uOl)WD4$d<>3OJJzb=1c54u9Y-e>YQeuy7)6KNy6k(p^8h3^#xcp`!WI3=P2REB zcL#1Z*cT&L!tCEp^*8@4$#h7m)x|Fy8I$m^f9G>GQ^%_D_`~*iJk>+z{$-Glj#`}BNf8mm zmYy;$3)6d8iv=M<0I$L>DiL_PgXY}eVRNYu#Y1a$3rkb(3K=B*HguO*U)_JVWMRWq z8!8ynv@_m+$Nmcv)(TJBGDc{`qvuUchq$KI*N^4$3}rT#9ycHRfaFF^@TporfD zy7ns5-F;r&=rV%u0sE`|!X_)`?IoJ-C43LkWfazh`S&pR&nW3Op}aFJwbgCcTY;*) zJchi=g}507@RkS`f@T?!0l4kHmwQ>vx^=Xma+k^FmnY|ogn)*FR7rMkf@0J*0slgl z7a%ZVUpdZiy12V6d^Wr%&=odwv!_r_j5MyG9Wo_h9}eT}LQHlX{iQ*M7ZbH~5ptgTNj;huw76JzaSPR)Mxzzqa* zN3+W;2A!^P=q+j)$FaUme+B$klXPBsN5b2*nlbj8j~@lm&vyQ*A@hT`*8)|eMQ{Gf z=KfpHgj*7Fm-n^zlLAi&#Ue_CU8U$L1qV)C-!h}B=q2@vLgnPG(6zt(S>?qH z1|rh`&K~JtY0l6w<-aa3!<;iSqofJ#ULC<1Sw91V;y`*6Vu2ki$AtWDM}%C3OCv|` zAT7nlYU@U?!)-M|`*3>%*n(X~J;Zs>(q}uhYcxjT>=>RC*F zlEN#^;)6`%+_M{yEx;&mx$F{Sf5Cn0HpPN&5j>m{n!fz%m>j>~l_~~-Ics!{#qpL) zor7SY^kmRpg;NzgiNs>GwW%dhx{|y9!zaNNdkslOY$-bk+n{I|<8Lte@DEG(1sGydi)qhPw+8q~P$KIXSJfYY!DEMmz{lh?T^xxv(s#o;u!lmRt7* zmVdFLE~{>L6~ns5>mY-Zi>__ktK5R&7{2D% zlgeA~6xW_CX&?Ot+MDo0;#Gj{?TXvWE?wMVMA&aG7*^oyxQ{WXV#XmRD)qBkLg_bz zArJZwfc9r@4|BQEo$f2&D_GPFqC?)rW1I$V_GB__mD*4FenR{+pt72R$W=DdulQk+ zA6l%?9dK6l1w>7t-o}*mBbZKH9T8}pl?MY4DI_R*r_8Ni=iO~`MDl~RXmn~H>ux1K zFOP2O@w=_D52=&_%$EgfRRCVyJN5BR`g>ee-+N#{*GGp77eBEjz6?+l@}28fP?qd zQf%3zJPYcm-wkljVpkkEDSP*vJqy(e{X%AWZ2SRM;MDg zhEiNW(SrPVxU`vas!ld^WNtxr1uiF4K%)EcTG^2g!Z&ocIZuXHt%l>kvcZIU%|{c6`Ot zf;2`k2?_Jnd#*=kpV_ zYXzlZ(sXJGa^slSVxxLglyyU#7b;w7&|!2~`!YUpD4$HNr3kNLWLRmQ|Ep5jQt%YD2=;9Y9A*j zC$pDapUXN1u~jsU4(Q$3lPQKdra+OjFGp8$6PDrYW!6g{<_vpph%BrjxR~@7&+#)m zZ=XicRokq^3tVpZE(Vm1)EG%#VzD#qb`{n@@1XaNlv3&=$HYqS#KtC3H28g43exIX z#!!ndgyY9S)iQ;ghZHW`k(0fJw~a*k`1oo zXE5`u-lSzv%wKuI(fA{p!Ljp}+we|o8&ZksJK#IopiC*&(c<3 zD`JBQ6gePL@isr?zDe!dZgoo5tCFh!bUM z0+MfSCE#i7uA5!|YpQZkzk&3@eNTk2Kv>0>@@6va>`+L2c8pMr*+}BuA+6q< zn^T-3Ei$E~llA@L;?F=j3JVn)iePkaSR8w)NxYrdJCNyTn_;@0{B^iqmF>2xw<7W% z9_NQ32HE;Ue>6{}i`mHoo7$9ck0xBA=;ccqr9h=Fm)8!m@|+B;X}6DjeO;7PptnMA z?#6Io>h8JP7d#wX8w#Lwwvv$vDyYoqd@u&{h*!R(PUN#~v!zXXwFNS<=X0EdB+<7P>tG@HK@~kX>0|$W!sP|F&U7vQ4C3t5%)xpYVQOg03+uBp{yKRhvu>FFznAxO6QxGSY+o__ z<2bPwrlzK{->d?R@GbEe_Oqc7p&%JqS@fuDju~rs*asca6OICeN$AFXiM?ee?5EF# z+krcFh?+y>4qy(~76cKZr?&rO>RauGG9SP!1|`H|Nb;*EjLP2DDWQ^h_0m!iFjcOV zZ)WHA4809fTmt7-H8ZRzZr!(c-HFyyl?L9tJhB?GX;-!e{ssrlv!}3U!7X4_v?Zyv zD@s=upb%Zbn({P$xJt6=|Qsaj*4aZl5vzf?N%)Y0yQimsRiJ}U3qIXT{> z#`-)#J4s~!{HdZL3Any|>eXyDigwn6TJ+rgqj=Yh_2HM_i=h6;Z5% z&2x*t!&q8Yj95Iyei0EPAt|{B^Ycl!QaIoWBwz^bL zTv(v|w<-Uc!Qvps(c|-Ap=!b^A_})NgAwGhl*gKYMGD)XibNQH<8%dlGU-E_014IT zT4xJW{IBy62q2rO(8L|;qKL4rn})YC|K=rt07MST<9;qFdr!}M&_x69ga5GD^LMY+ z_*v;%8?vdR)l*O_dU;iuL`<>Z)X|5-h@)$>de5CKJ)S0tB~LS0hF(%9=U>96n}W2 zsrIJ1Sy^qQa2di9$gaxC+kY;9TVAIX5H#O`DHo$CCT2S=8RJ8fv&-)TM_Z5w*Y)Mn zxs!GA*@KZmasTG>9i|&XsipomdiimBnj75ky#AmwLF%(E69w8Q0aMkkt`bSu))nRb zcOFWf2zL^FF!AD3x~-)n^pF3T`3BgFU?MUuxT+A66^+}4grs3!McWl>RstkqyZzd) zSl%vYft0vzvkdEV&QGa%Z>-44vS7J`@=iJVbPeTxR$5+GT2lo7y?VI~b8RNi?)3N& z2!;Mxo3lI78zi&!p-R!8m`ki1a-9JR~!{~aS6xyvB=Co z5Y$DJk^gyd===A*)9wL-^Y@Qv2Yqnf_QDflJuWqw3e#oBpdrcwv7wtj01lp*Sfzol zG&#BY^0MQje?ja|5GU;5Fw-8P;8;83b~HP^QucUzBxCis?O36~v}TL>qgRjQ?6@B+ zu>*%AVMb}jo%1CVd1+-^I78Dj?U=UNwf`vGwqng<7iUwuH);#URg*$BIg^9#jLo_d zBSd$fb-<; zH%c|Vb=~yL9qFfxhp(~Fz8`+=TXH&2@Rfr&IjPF$4ZihswS6du{eGH_P9*ahk9u9^O=vNND@(q%1sUy}Q;r+j^yKM1ahLpJX;hX6uB)p_G0B^0biMD_Ol z`tf%R2M=;e7-6)G^uN@m3$8$-6T79teRR^l#gXaL6x^DTH)9~TRY(;ZTI@ObkY_$ zVbRjOg7ln!i;WPI)n}(O+w!;l-jxf>Jn~#ysn#oN|9P}9CqR!eU|)@I2*^tP^t9Nm zf&nz5ZPsRjH!UQ@RfNS+Q?A!dMzeya@xIpAxn7!tXZnFA`Cn?;1!JktLeaGC{g>UR z%j0h7d<5Lr#Mx?zNt=MFphp2rRbSsXJX3n%F>gYGaMbBMJHG8@Up`5NT5#;zg^NCE znfBS6-qAUTMEH16!2)0he`N{g!B$mlDNx@zxjkDbXLvTm%z9$#+fmWD5Z^@l3_7TBgZ|ldgXe_^`w!n7 zuYlGSPGr5-bfv{o^>lT0bqvv2KCp^$MQ+2Bu|3rT4AleP&|=Rnv%yRPj3p>mO!H=n zkw1nFZ2n95;K)$WC?b$u<(R)Zi6 zgNCa-JsJVwJ8t5$ieLVK1%=pAh}2nwj3`K9A`6W6v0FTQ`ZJADAR9Xa#OvRurW)S8 z3*uuDV;UD*i3CwB9a-VRK04KgYwT9Ulp##4@;mN@em@j>YRp7ckXC zdm&M3*w)s@-nZlb;dYL0n?r{NcAys`(t(em9m&nR^fJg5Qg2`N8C}@9b)1bTO2B5h zO(B)HjJBLDU52fMS`rf)O7nDtjjv98FYda@d3#!u>dnUH`|;!r6g)u&9J_AP)zolV zeR0gn3W6d6QZGmgBt&GMeHA|XTBM1M=F;tMb4jpsdfBfDb`HRALG#5UOlo*&06NB8 z^olJ$UK$3fO_g`hDv8+H5eacu6{?E6CZjNM;udE0Kg7uR>uF1kvskb_czU`-MJ5*{ z$N&g^BpT&KsMu5FHp{8hKEt>M6|cdFcWo5)*JcTA`#Yc4C~?-R{iIe;xqgdOk}3Zghq zDXNNhMUU}PAI0$TFd?DBp?ATp)u99AszA1dw-4U>z?7>Zk$J-|X8pY_U|*pX9+!R= zd9<0gH#@J&JBLdLThkyVf-C+`2qW5wH@1Ucwz1opiid&h};OA# zz0h9eawbP_1}nvI;>ev2?>BDD)}YOyV&r`w*2B=k?ocvd_x5nts%$Hx9Uuf&fAOC5 zUQT8&XaU>PnukKVpO@~M&0{o7dNw!vv~7y>RT<( zQ|A9oKJU&J^=|4&v~zFyJS&0#3MOWDt1E*j!D$RqQ6=~RbK&{NheCr_ZM90=9}b^G zOWe8W$t`@{*^jeG=^4Ctg%^gL{>}_-v9s8{m(t`d+{`Q>6pZ=aFb7{$QP?Oz#ENa7 zJC}ff7n{hJL*C4JT^<5ipE&GJ3V%LcA->ISV*1u08?p}AqQb(ZRH*TPa z=cD90bM5&YkY|K~ifu|Aj{~-+SF!M%1T(%eAY3Q}=7JV+5fRo-$s@D)8`X*}^vTW8 zs8r10(o2-q3zWXm`bae+e??a4Jt9b$PUO44`B6`OFOUqHM3kj;1@+Im${*H7|G0>M zfFilJ=NEE8UsgaD=eSy?e| zd-ti?S5vn$ZS$7Vxf*+!-ub*W`Qy+2%*_$GnqALrOEYXVx}0mK>RX2BYQ)Csn;2_` z{$jF(GQ$Q%&+}~dXC%8s7p;PB;)PJor}`fx+=I{I)@6fXfj*ig&$W!sb0<`ZZYNb< zhg7|S>`S>R4!>`!!%4--$(AS|%B4uz^dgL(8XG~fAvY^6&Nm6R$t;~v!$}HNak8YT zZ%i@6wu4Jc1a#-{{MpN!2F;u-di<}Q*p!yczL#5hYH{q?)Vd+}#V@WSAtp@aq~4bw z9SBK>#sbekhJk$yuCpzE`-i@M4gOk6B_OyV&igL6INXDH(vtMs+@p#!FEH92uUN%Fe`fjnM-Nj3BUQYMA5s8Q zkk6E^6*ze(@<}>|=Frd*H4b|@J=fnLPcT{gTs9y)#n)$bUiKv9qO<8J?*o_t6dnKk znq7|5qk-ue0{|MDq^)Iegke1MZJ7I^Fcr~SJGnOSZSF8zi0u2C6kYkm`eJ(Hm{$&c zZ9m(Z&7+o(VVUZ$7uunIHpe!j+2VCy`@5rulGfIw!nP;KALbDI%c~#fYoT^#c~x= ztXtp;pXV-dTi3*Dy}HAxCEu%}cBPI%YrC!fVhguG5z^H__D9zVEV4;&v-%rFSdU=| zJfEndgVg@Og!WM<&To})+T-346(w!%B;^n9tQhR=@Av1XI$9{6S>f=G_YkYm?N(_D z^>=VbV(-EGzF%ga`C@7j#q5>d$@D2QGQ~hV;sHXoq#s_t@H%`ZsQ! zq@zzW(bGF^oMQ3%m-&^ki#vJgNWd+3PmCe?D@jG@+xz98<-|N&Kk)5kA?6yW)x&yN zLXNh2#!Nho5{?_U2RyHE`seP}c)hxKXh9HWSXI>@Ce>+qRgEr{nfQ_;Z}f8C&CiXv z69+B`FQI#7KTAZ5xo&YRFBz>M_y0CF3V7H*5WA5LuGvp7Q=N<$%0J6-UV6KbW%APLc- zd3PeB%bvAE1PuHxkWGR!2>0&saC24`7i0oO zX%P4bVWN=XqeJCW&a*VA$qDH8|d%&{BH2ZPRsYpFS!aO6xlVu1`DvV!H{h@)ub$!+Ci=F8= z=SAi6W7*1dndZ=X{s#t6?P}8`4dGXZ*&1Lg;(HTErquQ#y8t;iM5M(LGuiie9T2ki zSSaYheRSQ6h@YEe*3nfHh!F4j^F8JLO(S!blT{}`^(!=%Qt zHrpTYzJk9r{Xy2F7mSeVVe=|dE9eeN^5I`&fsrvHBI$^jYwNEj$6e`DCrG%1hGJDH zAZD62F1P&Vn4Wz#mkc;jZDB{E?I_DMvg1iw>e$u8%(sLU_o03J-fexaMv$6hNj&5DJYiZ3hW4?m!6qpHzX`DbTsW|XeKG8G z)_s4XfyYZ4(tk|iADk;+PYGEu7QhMya#IppXAAt;L8G#BajqR%W0&_5c{Pv~&_zk}+x&zD44{gO?W()bFX94k zLS93AE%QrZqCp!hB>@Kj8c6xB%cg~2=5dTo@8zPOf>3b-}VwZ60jeT zFM`#D+F>&29n*hDZLvdc+mmgi&PfWoKRll{G5ykk6p~4TSa+|4>M7ssi?L8w0IM?G z;V|oz7|}I|(L#x$oE&Dds(I&h`zZ3hk}VBYWVJ3{y!d5QGMes*xSQtYrZcU~@JA^< zs?U;(uuvA-W{1L!5aHs#@fZ)K2m4N|Iqu5c+aEUI_**s$I8An;aR1vEy%NH`v^FQB7Aylhr+{`wc( zQpZ~aYiUbZhbHgW(lWX*osb7^0k#vpjnLBqiMs6n!GgyLu98=c=Y(UL zj+S|$mGI0LC_eO>U51Cb$XYiPByuQ8Fs0NtDBc)un6vU@%--O z(H7}}+6ILMafo6=J-_iS)e5pjY#VSLrt|#em_*j#fVF68bxbi7pFA0f!fb5o*BGxK zxJNm>26uREBJwRoJ^CWwzcM4s|zpm=pjUQJJX`=9>q(-||s9JcHZWdr8Wxx(I5j8&%CGoUhu zO-8;IvOLO-jH)VyFKbUGQVd4u46bcj2qtnW5lXssSk^Di5?aTldg&@3yPM-b4+e&kZaw9@SMQ3ytF>G;e{z%|5>cj+cN13ICJtjvd<@BiOTBCg7N+yc2`&V z``GHDnUcppoM4uG)mSed(_{kkAj^xUgOy>A()UujA zaV)>2B*6TDb144)ecj9~ad=Zhetf+p$L)MG_V^u}S7{f5HZe}$n)I-F z)p+6;-)p1W4);RnEZHVD7pAgFL_TfTiObSzx#H<90a22u^EDY==PWDlPwj?}qqQd6 zGacXe53H@+tlp=uC9BC(X2cvX+w<`0a7IL0t`k>rH+^#YSktl>O}YOPourf$;PFE9 zUrLdWo-Cvsx-oI?{aTzn%n7BxC;BJ2M(kx4das|=nBW&SGIrx@IzUUQ@|p<74&v2g zrKSAOeZ%$VL%=iy&^s6o&uvAEC${DwqVb}1vEKtGl^`hPIpl8878Mmi;iT&Atw{3K zpWXIV#3_YXzK>fUP?nx+IrgjTl)^HF^tz#jm6g@+-?APiB@a4Nn*pFe4u@Y_2{Osc=4p{V3HTqYUWz9L7>z6J zOkYYd47tFa;|sOalQ!h6kYqcP6%~ov%QF158hontCo@tbGq~##DeYMb-(=78pFbKk zij;n~X0~~z)exJMbW9*3GlQkJk2i=HXdI$I=sD#xvJa7~$$#H0Q2>;Xl z@zW>DuR}K#qBKUVMUID)zwAro>;FDk6~-{f-yfKgLGv1F>yKSsH9wq8FI^&;a%=S< zG4j;YLhk_N#IFll6@qhf_l>y7%E>tZ&Ny*@KE%$G?BYsK7tW4#y!m9W>TK`e;2d53 zg>yA=fLZNY(1viLGSt&MfTH4thh~Zk3Mr z{xZ1`TpUobBvF;&{`nUM4UQvbA|1`v?STt8-erl-lUgqH9C1Pi+*iFZw9~ni-1rTFD$O-2{J}(oJL$gi$&AS%f_*9A;U6|-A~J3Wbyy-l80lx zI_zZCmM8g~x6m)Y*`$d6N%4ZW=)wnyfTtgwot=ODS$=W0_~5zSQKYpT226E9nM~MN z;5x)f2@{BaraD#&?6#$w;M3{vm^w2@jVrqR*xqC` zDA4&FA=ymN@ganvWkjgqlOV6gNiD4u5Yd9R@f8DW`=U?mv6cI)V|ry#F!(v&Oyh)- zu%v%ck@uatc6xttpE163hVCSNeTz(PwW*sL{7fV+Onk9mB_r8M%cz3cT}!w|FIcl^ zAcQ5tjFň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 z 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 literal 0 HcmV?d00001 diff --git a/tf/1/output_38_0.png b/tf/1/output_38_0.png new file mode 100644 index 0000000000000000000000000000000000000000..76a98f2511c9761491d98ae279bd6d0a886d4683 GIT binary patch literal 133778 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

o^R;?qGf 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 P+kJcXtKO#`yYPPi-DUJ+ literal 0 HcmV?d00001 From c31e6887c3d7efc2bd39dc9bb27f106ba89e504f Mon Sep 17 00:00:00 2001 From: haohu Date: Thu, 7 Mar 2019 15:11:46 +0800 Subject: [PATCH 017/137] x --- tf/1/1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tf/1/1.md b/tf/1/1.md index f9c4cfee..7cec154c 100644 --- a/tf/1/1.md +++ b/tf/1/1.md @@ -1,5 +1,5 @@ -# TensorFlow Hello World 平面你和 +# TensorFlow Hello World 平面拟合 http://www.tensorfly.cn/tfdoc/get_started/introduction.html From 6e10c36e5a17dac3467ecffcdcd1b922899a56c3 Mon Sep 17 00:00:00 2001 From: haohu Date: Sat, 23 Mar 2019 10:11:55 +0800 Subject: [PATCH 018/137] x --- css/webide-layout.html | 44 ++++ mypost/graduation.md | 108 ++++++++ mypost/programmer_graduation.md | 45 ---- mypost/tech_note.md | 449 +++++++++++++++++++++++++++++++- 4 files changed, 598 insertions(+), 48 deletions(-) create mode 100644 css/webide-layout.html create mode 100644 mypost/graduation.md delete mode 100644 mypost/programmer_graduation.md diff --git a/css/webide-layout.html b/css/webide-layout.html new file mode 100644 index 00000000..eee43c35 --- /dev/null +++ b/css/webide-layout.html @@ -0,0 +1,44 @@ + + + + test + + + + + +
+
+
+ + diff --git a/mypost/graduation.md b/mypost/graduation.md new file mode 100644 index 00000000..ddc0fd9d --- /dev/null +++ b/mypost/graduation.md @@ -0,0 +1,108 @@ +# 程序员成人礼 + +## 编程基础 + +- 实现数字 + - 数字的二进制表示及应用 + - 只用0和1的list(bitmap)表示数字,或者直接list长度表示数字,百年语言 + - linux 权限表示 + - modbuf 协议解析里的CRC,ID 卡通信协议的BCC,其它校验函数 checksum 等 + - 编程珠玑 bitmap 统计号码 + - 数据库 bitmap join index 提高性能 + - redis 内部的压缩数字 减少空间使用 + - 子网掩码 + - tcp 协议标志位 减少空间占用 + - Binary Indexed Tree + - 各种语言里的 bitmap, bitset +- 实现字符串 +- 实现时间 +- 实现列表 +- 实现字典 +- 循环样式程序 +- 函数式编程 +- 二分查找 +- 快速排序 + +### 数字 + +How do you set, clear, and toggle a single bit? +https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit +Bit Twiddling Hacks +http://graphics.stanford.edu/~seander/bithacks.html +BitSet的用法 +http://www.cnblogs.com/happyPawpaw/p/3823277.html +BitMap +https://blog.csdn.net/kl1106/article/details/79478787 +Linux权限详解(chmod、600、644、666、700、711、755、777、4755、6755、7755) +https://blog.csdn.net/u013197629/article/details/73608613 +Modbus通讯协议学习 - 认识篇 +http://www.cnblogs.com/luomingui/archive/2013/06/14/Modbus.html +BCC(异或校验)、CRC、LRC校验算法 +https://blog.csdn.net/windows19790408/article/details/5787039 +位图(BitMap)索引 +http://www.cnblogs.com/LBSer/p/3322630.html +关于 Bitmap join index +http://www.sizhxy.com/m/NewsCenter/NewsDetail-278.html +PHP实现 bitmap 位图排序 求交集 +https://www.cnblogs.com/iLoveMyD/p/4167623.html +MySQL数据结构分析—BITMAP +http://blog.chinaunix.net/uid-26896862-id-3443593.html +ip地址及子网掩码换算,子网划分教程 +https://jingyan.baidu.com/article/ae97a646d936ddbbfd461d02.html +Redis底层数据结构 +https://zhuanlan.zhihu.com/p/38380467 +Redis 设计与实现¶ +http://redisbook.com/index.html + + +转换成八进制数,则为 r=4, w=2, x=1, -=0(这也就是用数字设置权限时为何是4代表读,2代表写,1代表执行) + +实际上,我们可以将所有的权限用二进制形式表现出来,并进一步转变成八进制数字: + +rwx = 111 = 7 +rw- = 110 = 6 +r-x = 101 = 5 +r-- = 100 = 4 +-wx = 011 = 3 +-w- = 010 = 2 +--x = 001 = 1 + + + +## 网络基础 + +- tcp echo server +- web server +- 多路复用模型 +- 多线程,同步,锁 +- 日志 +- 无锁队列 +- 内存池,零拷贝实现 +- 多进程,进程通信 +- 信号处理,优雅重启 + +## 编译解析 +- 状态机,url 解析, +- 配置解析,ini,xml,json +- 抽象语法树 +- 正则引擎 +- http 协议解析 +- 实现一个模版 +- 实现一个脚本 + +## db + +- db 实现 +- 索引实现 +- 二分查找 +- 树查找 +- SQL 解析 + +## MVC + +- Route +- Model +- View +- Controller +- Library +- Helper diff --git a/mypost/programmer_graduation.md b/mypost/programmer_graduation.md deleted file mode 100644 index 62d66be1..00000000 --- a/mypost/programmer_graduation.md +++ /dev/null @@ -1,45 +0,0 @@ -# 程序员成人礼 - -## 编程基础 - -- 实现数字 - - 数字的二进制表示及应用,linux 权限表示,modbuf 协议解析,编程珠玑 bitmap,数据内部的 bitmap -- 实现字符串 -- 实现列表 -- 实现字典 -- 日志 - -## 网络基础 - -- tcp echo server -- web server -- 多路复用模型 -- 多线程,同步,锁 -- 无锁队列 -- 内存池,零拷贝实现 -- 多进程,进程通信 -- 信号处理,优雅重启 - -## 编译解析 -- 状态机,url 解析, -- 配置解析,ini,xml,json -- 正则引擎 -- http 协议解析 -- 实现一个模版 -- 实现一个脚本 - -## db - -- db 实现 -- 索引实现 -- 二分查找 -- 树查找 -- SQL 解析 - -## MVC - -- Model -- View -- Controller -- Library -- Helper diff --git a/mypost/tech_note.md b/mypost/tech_note.md index 4788c5a9..aa18ec0a 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -1561,7 +1561,7 @@ https://www.cnblogs.com/dzlixu/p/5e9475c3990d720ca22e18b730b01d57.html sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ssh.1.uoyp9smajh939e98yc40ygm5z - + sudo sed -i "s|EXTRA_ARGS='|EXTRA_ARGS='--registry-mirror=http://1d20ae13.m.daocloud.io |g" /var/lib/boot2docker/profile @@ -2202,7 +2202,7 @@ ubuntu使用apt-get制作OfflinePackage https://blog.csdn.net/ouyangziling/article/details/79056161 -缺少rsync也可以通过校验和比较文件。 +rsync也可以通过校验和比较文件。 --size-only这意味着rsync将跳过大小匹配的文件,即使时间戳不同。这意味着它将同步比默认行为更少的文件 @@ -2240,6 +2240,7 @@ https://blog.csdn.net/qtlyx/article/details/80614608 Best approach to encrypt big files with php https://stackoverflow.com/questions/16175154/best-approach-to-encrypt-big-files-with-php +加密解密 Whole File Encryption/Decryption With PHP http://monkeylogic.com/whole-file-encryptiondecryption-with-php/ @@ -2285,4 +2286,446 @@ https://blog.csdn.net/oalevel/article/details/81834837 用友T3 -http://www.3322.cc/soft/43731.html \ No newline at end of file +http://www.3322.cc/soft/43731.html + +Redis 设计与实现¶ +http://redisbook.com/index.html + +技术图书作译者的炼成方法 +http://blog.huangz.me/2017/how-i-became-a-writer-and-translator.html + +《Redis in Action》翻译记事 +http://blog.huangz.me/diary/2015/memories-of-redis-in-action-translation.html + +分布式监控工具Ganglia 介绍 与 集群部署. +https://www.cnblogs.com/yuki-lau/p/3201110.html + +数据可视化(三)基于 Graphviz 实现程序化绘图 +https://riboseyim.com/2017/09/15/Visualization-Graphviz/ + +全面学习Prometheus +https://www.sohu.com/a/233111809_198222 + +Cacti:cacti是用php语言实现的一个软件,它的主要功能是用snmp服务获取数据,然后用rrdtool储存和更新数据,当用户需要查看数据的时候用rrdtool生成图表呈现给用户。 +Nagios:Nagios 利用其众多的插件实现对本机和远端服务的监控,当被监控对象出现异常,Nagios 就会及时给管理人员告警。 +Graphite:Graphite 是处理可视化和指标数据的优秀开源工具。它有强大的查询 API 和相当丰富的插件功能设置。 + +Kibana:Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据 +Grafana:Grafana是一个跨平台的开源的度量分析和可视化工具,可以通过将采集的数据查询然后可视化的展示,并及时通知 + +Zabbix:zabbix 是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案 +Ganglia:Ganglia是UC Berkeley发起的一个开源集群监视项目,设计用于测量数以千计的节点。 +Prometheus:Prometheus 将所有信息都存储为时间序列数据,实时分析系统运行的状态、执行时间、调用次数等,以找到系统的热点,为性能优化提供依据。 + + +通过建立完善的监控体系,从而达到以下目的: + +长期趋势分析:通过对监控样本数据的持续收集和统计,对监控指标进行长期趋势分析。例如,通过对磁盘空间增长率的判断,我们可以提前预测在未来什么时间节点上需要对资源进行扩容。 +对照分析:两个版本的系统运行资源使用情况的差异如何?在不同容量情况下系统的并发和负载变化如何?通过监控能够方便的对系统进行跟踪和比较。 +告警:当系统出现或者即将出现故障时,监控系统需要迅速反应并通知管理员,从而能够对问题进行快速的处理或者提前预防问题的发生,避免出现对业务的影响。 +故障分析与定位:当问题发生后,需要对问题进行调查和处理。通过对不同监控指标以及历史数据的分析,能够找到并解决根源问题。 +数据可视化:通过可视化仪表盘能够直接获取系统的运行状态、资源使用情况、以及服务运行状态等直观的信息。 + + +elk +日志存几天,可以按天分区的,定时删 +其它运维指标和运营指标存的时间比较久 +单元测试和拨测成功率都扔es里,每天发报表到小组邮件里 +还有每天的 Error 日志 loggerName, 慢速 api,慢速 sql 的top 10 +每个规模指标,收入指标,LoggerName,API 性能,慢 SQL 都有对应负责人 +日志组件是 error 日志 es, db,本机文本都记一份,warning 日志只记 es 和 本机文本,debug 只记录到本机 +查问题先查 db,然后es,最后写了个分布式 grep 可以在web界面上对多台机器上同时进行 grep,前提是每台机器的日志的相对路径是一样的,而且使用者在cmdb里有这几台机器的权限。 + + +【技能】小白耳机维修入门--各种耳机插头接线图--耳机维修汇总贴 +https://www.cnblogs.com/tony-ning/p/7445761.html + +科普帖:深度学习中GPU和显存分析 +https://blog.csdn.net/liusandian/article/details/79069926 +深度学习需要的显卡配置 +https://blog.csdn.net/pjh23/article/details/83513066 +【深度学习】为什么深度学习需要大内存? +https://blog.csdn.net/shenxiaolu1984/article/details/71522141 + +Docker环境下玩转GPU(一) +https://www.jianshu.com/p/2442394a934e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation + +ML之NN:BP solve XOR Problem +https://blog.csdn.net/qq_41185868/article/details/80789982 +【机器学习】神经网络实现异或(XOR) +http://www.cnblogs.com/Belter/p/6711160.html + +不用算术运算符实现两个数的加法(按位异或) +http://www.cnblogs.com/houjun/p/4908725.html + + +Codeup墓地 Online Judge FAQ +http://codeup.cn/faqs.php +OnlineJudge 2.0 +https://github.com/QingdaoU/OnlineJudge/blob/master/README-CN.md +模型评估指标AUC(area under the curve) +https://blog.csdn.net/liweibin1994/article/details/79462554 + + +获取 group by 后的序号,第几组的第几条数据: + +SELECT +@exp_number:=CASE WHEN @task_id=task_id THEN @exp_number+1 ELSE 1 END AS exp_number, +@task_number:=CASE WHEN @exp_number = 1 THEN @task_number+1 ELSE @task_number END AS task_number, +@task_id:=task_id AS task_id, +CONCAT(@task_number, '.', @exp_number) AS seq, +exp_id +FROM exp_course_class,(SELECT @task_number:=0, @exp_number:=0,@task_id:='') AS t +WHERE course_id = 41 AND class_id = 40 GROUP BY task_id, pos, exp_id +ORDER BY task_id, pos, task_number, exp_number; + +线性代数——向量、向量加法、向量数乘 +https://blog.csdn.net/Fancy_Real/article/details/79944916 +线性代数的本质学习笔记(1):向量、线性组合、张成(SPAN)、线性变换 +https://blog.csdn.net/dugudaibo/article/details/78714668 + +矩阵-DirectX与OpenGL的不同 +http://www.cnblogs.com/graphics/archive/2012/08/02/2616017.html + +## 数学 +物理专业:向量是空间中的箭头,由长度和方向决定。可以自由移动;数学专业:向量可以是任何东西,只保证向量加法和数乘运算有意义;计算机专业:向量是有序的数字列表,起始点在原点上。 + +所有可以表示为给定向量线性组合的向量集合被称为给定向量张成的空间。 + +两个向量在三维空间中往往构成的是一个平面,有的时候也可能是一个直线。三个向量一般在三维空间中张成了整个三维空间,可以想象一下,两个向量已经张成了一个平面,而平面沿着第三个向量进行平移,张成了整个三维空间。新增的向量如果落在了原有向量张成的空间中,那么这个向量与原先的向量是线性相关的;另一方面如果所有向量都给张成的空间增添了新的维度,它们就是线性无关的。 + +向量在右 矩阵在左的时候 + +线性变换就是把矩阵里的值看成列向量 + +空间投影就是把矩阵看成行向量(每个行向量和右边向量点积,点积即投影) + +首先,线性变换是把一个向量变为另一个向量,这里不涉及坐标变换。形式上,你可以把向量写成一行,然后右边乘一个矩阵,结果仍然写成了一行,成为新的向量。至于向量左乘矩阵,当这个向量的每个分量都表示一个基的时候,这就是基变换;而当这个向量的每个分量只是向量分量时,这就是线性变换。 + +线性组合 线性变换 线性相关 线性可分 空间投影 + +OpenGL中经常会使用的平移矩阵、缩放矩阵以及旋转矩阵,投影矩阵。 + +解析解,是指通过严格的公式所求得的解。即包含分式、三角函数、指数、对数甚至无限级数等基本函数的解的形式。给出解的具体函数形式,从解的表达式中就可以算出任何对应值。用来求得解析解的方法称为解析法,解析法是常见的微积分技巧,如分离变量法等。解析解为一封闭形式的函数,因此对任一独立变量,皆可将其代入解析函数求得正确的相依变量。因此,解析解也称为闭式解。 + +特别的,凸集,实数R上(或复数C上)的向量空间中,如果集合S中任两点的连线上的点都在S内,则称集合S为凸集。 + +凸函数是一个定义在某个向量空间的凸子集C(区间)上的实值函数f,而且对于凸子集C中任意两个向量 , f((x1+x2)/2)>=(f(x1)+f(x2))/2,则f(x)是定义在凸子集c中的凸函数(该定义与凸规划中凸函数的定义是一致的,下凸)。 + +牛顿法是一种在实数域和复数域上近似求解方程的方法。方法使用函数f(x)的泰勒级数的前面几项来寻找方程f(x) = 0的根。牛顿法最大的特点就在于它的收敛速度很快。 + +梯度下降法实现简单,当目标函数是凸函数时,梯度下降法的解是全局解。一般情况下,其解不保证是全局最优解,梯度下降法的速度也未必是最快的。梯度下降法的优化思想是用当前位置负梯度方向作为搜索方向,因为该方向为当前位置的最快下降方向,所以也被称为是”最速下降法“。 + +梯度的本意是一个向量(矢量),表示某一函数在该点处的沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。 + +在函数定义域的内点,对某一方向求导得到的导数。一般为二元函数和三元函数的方向导数,方向导数可分为沿直线方向和沿曲线方向的方向导数。 + +启发式优化方法种类繁多,包括经典的模拟退火方法、遗传算法、蚁群算法以及粒子群算法等等。 + +共轭梯度法是介于最速下降法与牛顿法之间的一个方法,它仅需利用一阶导数信息,但克服了最速下降法收敛慢的缺点,又避免了牛顿法需要存储和计算Hesse矩阵并求逆的缺点,共轭梯度法不仅是解决大型线性方程组最有用的方法之一,也是解大型非线性最优化最有效的算法之一。 + +共轭在数学、物理、化学、地理等学科中都有出现。 本意:两头牛背上的架子称为轭,轭使两头牛同步行走。共轭即为按一定的规律相配的一对。通俗点说就是孪生。 + +作为一种优化算法,拉格朗日乘子法主要用于解决约束优化问题,它的基本思想就是通过引入拉格朗日乘子来将含有n个变量和k个约束条件的约束优化问题转化为含有(n+k)个变量的无约束优化问题。拉格朗日乘子背后的数学意义是其为约束方程梯度线性组合中每个向量的系数。 + +最优化问题的共同特点是:求满足一定条件的变量x1,x2,…,xn,使某函数f(x1,x2,…,xn)取得最大值或者最小值。 + +矩阵A为n阶方阵,若存在n阶矩阵B,使得矩阵A、B的乘积为单位阵,则称A为可逆阵,B为A的逆矩阵。若方阵的逆阵存在,则称为可逆矩阵或非奇异矩阵,且其逆矩阵唯一。 +在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。除此以外全都为0。 + +矩阵求导(Matrix Derivative)也称作矩阵微分(Matrix Differential),在机器学习、图像处理、最优化等领域的公式推导中经常用到。 + +矩阵的微积分本质上是多元变量的微积分问题,只是应用在矩阵空间上而已 + +机器学习中的线性代数之矩阵求导 +https://blog.csdn.net/u010976453/article/details/54381248 + +参数估计是机器学习里面的一个重要主题,而极大似然估计是最传统、使用最广泛的估计方法之一。 + +十分钟学习极大似然估计 +https://endlesslethe.com/easy-to-learn-mle.html + +极大似然估计其实是理想地认为,对于极少的样本观测,我们观测到的样本很可能就是发生概率最大的。 + +Latex数学公式简明教程 +https://endlesslethe.com/latex-math-formula-tutorial.html + +最小二乘法是勒让德( A. M. Legendre)于1805年在其著作《计算慧星轨道的新方法》中提出的。它的主要思想就是求解未知参数,使得理论值与观测值之差(即误差,或者说残差)的平方和达到最小: + +把 矩阵的行列互换之后得到的矩阵,称为 的转置矩阵,记作 A^T +一个n阶方阵A称为可逆的,或非奇异的,如果存在一个n阶方阵B,使得AB=BA=E则称B是A的一个逆矩阵。A的逆矩阵记作A-1。 + + +3D矩阵变换中,投影矩阵是最复杂的。位移和缩放变换一目了然,旋转变换只要基本的三角函数就能想象出来,投影矩阵则很难凭借直觉想象出来。 + +当样本量m很少,小于特征数n的时候,这时拟合方程是欠定的,需要使用LASSO。当m=n时,用方程组求解。当m>n时,拟合方程是超定的,我们可以使用最小二乘法。 + +投影矩阵推导(翻译) +https://www.cnblogs.com/davelink/p/5623760.html + +损失函数(loss function)或代价函数(cost function)是将随机事件或其有关随机变量的取值映射为非负实数以表示该随机事件的“风险”或“损失”的函数。在应用中,损失函数通常作为学习准则与优化问题相联系,即通过最小化损失函数求解和评估模型。 + +机器学习:Python中如何使用最小二乘法 +https://www.cnblogs.com/lc1217/p/6514734.html + +机器学习:形如抛物线的散点图在python和R中的非线性回归拟合方法 +http://www.cnblogs.com/lc1217/p/6519860.html + + +机器学习:Python实现单层Rosenblatt感知器 +https://www.cnblogs.com/lc1217/p/6530177.html + +Rosenblatt感知器详解 +https://www.cnblogs.com/lanix/p/5003521.html + +Python 机器学习 +https://www.cnblogs.com/lc1217/category/960537.html + +深度学习:Keras入门(一)之基础篇 +https://www.cnblogs.com/lc1217/p/7132364.html + +常见硬盘IOPS参考值 +https://elf8848.iteye.com/blog/1731301 + +对机械硬盘和SSD固态硬盘IOPS、吞吐量的压测对比 +http://blog.itpub.net/28916011/viewspace-2200027/ + +硬盘IOPS与读写速度 +https://blog.csdn.net/star890124/article/details/52004138 + +磁盘性能指标--IOPS、吞吐量及测试 +https://blog.51cto.com/wushank/1708168 + +centos7 df 命令卡死 +https://www.cnblogs.com/John-2011/p/9577038.html + +systemctl restart proc-sys-fs-binfmt_misc.automount; + +调用谷歌翻译API +https://www.jianshu.com/p/6187d5915f70 + +解决pip的警告 +pip install pyopenssl ndg-httpsclient pyasn1 + + + +禁用 root ssh,增加新用户 + +useradd wawa +passwd wawa +usermod -G wheel wawa + +vi /etc/ssh/sshd_config + port 36000 + PermitRootLogin no + + +用sklearn做一个完整的机器学习工程——以波士顿房价预测为例。(一、用自定义转换器、Pipeline Feature_Union做特征工程) +https://blog.csdn.net/PythonstartL/article/details/82874932 +https://blog.csdn.net/PythonstartL/article/details/82991548 +https://blog.csdn.net/PythonstartL/article/details/83347173 + +https://blog.csdn.net/sdoddyjm68/article/details/78991699 + +香港大学深度学习课件笔记(1.5) +https://blog.csdn.net/sdoddyjm68/article/details/78409202 + +总结:sklearn机器学习之特征工程 +https://www.jianshu.com/p/1c4ec02dd33f + +Sklearn 笔记 +https://www.cnblogs.com/yaoz/tag/Sklearn/ + +机器学习中的范数规则化之(一)L0、L1与L2范数 +https://blog.csdn.net/zouxy09/article/details/24971995 +笔记︱范数正则化L0、L1、L2-岭回归&Lasso回归(稀疏与特征工程) +https://blog.csdn.net/sinat_26917383/article/details/52092040 + +机器学习中的损失函数 (着重比较:hinge loss vs softmax loss) +https://blog.csdn.net/u010976453/article/details/78488279 + +zouxy09博客原创性博文导航:怀着对机器学习和计算机视觉 +https://blog.csdn.net/zouxy09/article/details/14222605 + +Python机器学习库scikit-learn实践 +https://blog.csdn.net/zouxy09/article/details/48903179 + + +iris-经典案例解析-机器学习 +https://www.jianshu.com/p/da18f0cd7f60 + +机器学习训练数据 +http://archive.ics.uci.edu/ml/index.php + +tensorflow提示未编译使用SSE4.1,SSE4.2等问题的解决方法 +https://blog.csdn.net/qq_36511757/article/details/77895316 + + +Ubuntu 18.04 安装 nodejs +Ubuntu 18.04 LTS Server npm : Depends: node-gyp (>= 0.10.9) but it is not going to be installed [duplicate] +https://askubuntu.com/questions/1057737/ubuntu-18-04-lts-server-npm-depends-node-gyp-0-10-9-but-it-is-not-going + +ubuntu无法修正错误,因为您要求某些软件包保持现状...解决办法 +https://blog.csdn.net/buppt/article/details/78914234 + +sudo apt install npm + + npm : 依赖: node-gyp (>= 0.10.9) 但是它将不会被安装 + +sudo apt install node-gyp + + node-gyp : 依赖: nodejs-dev 但是它将不会被安装 + +sudo apt install nodejs-dev + + nodejs-dev : 依赖: libssl1.0-dev (>= 1.0.2) 但是它将不会被安装 + +sudo apt install libssl1.0-dev +sudo apt -f install npm + + +How to install Node.js and npm on Ubuntu 18.04 +https://linuxize.com/post/how-to-install-node-js-on-ubuntu-18.04/ + + +## 小型团队工程化 CheckList + +git 不显示中文 +git config --global core.quotepath false + +tar 压缩中文文件乱码 +sudo apt install p7zip-full +7za a x.7z *.ipynb *.py + + +Issue in installing php7.2-mcrypt +https://stackoverflow.com/questions/48275494/issue-in-installing-php7-2-mcrypt +https://lukasmestan.com/install-mcrypt-extension-in-php7-2/ + + +apt install php-pear +pecl version + +apt-get -y install gcc make autoconf libc-dev pkg-config +apt-get -y install php7.2-dev libmcrypt-dev +pecl install mcrypt-1.0.1 + +bash -c "echo extension=/usr/lib/php/20170718/mcrypt.so > /etc/php/7.2/cli/conf.d/mcrypt.ini" +php -i | grep "mcrypt" + +Encrypt/decrypt with XOR in PHP +https://stackoverflow.com/questions/14673551/encrypt-decrypt-with-xor-in-php + + +2018-04-27 《程序员的职业素养 - The Clean Coder》 +https://blog.csdn.net/sunzhongyuan888/article/details/80396825 + + 好程序员与坏程序员的差别详细分析! +http://www.sohu.com/a/223781695_413876 + + 什么样的人当不好程序员? + https://36kr.com/p/5042433.html + + 一个十几年程序员给所有新老程序员的忠告 + https://www.jianshu.com/p/57fd54974d71 + + 好的程序员和差的程序员 + https://blog.csdn.net/dutao53486944/article/details/20009039 + + 金牌员工下班前必做的6件事 + https://jingyan.baidu.com/article/bad08e1ea2e3a709c9512168.html + + + # 格式化大硬盘,大于 4 G + + # 查看硬盘数 + fdisk -l + + # 用 parted 进行大分区 + parted /dev/vdb + mklable gpt + print + mkpart primary 0 4295GB + gpt + + # 查看分区结果 + fdisk -l + + # 格式化 + mkfs.ext4 -T largefile /dev/vdb1 + + # 挂载 + mount -t ext4 /dev/vdb1 /data2 + + # 设置重启后也生效 + echo '/dev/vdb1 /data2 ext4 defaults 0 0' >>/etc/fstab + +ganglia 不显示堆叠图 +Missing stacked graphs on new ganglia-web 3.5.3 install +https://sourceforge.net/p/ganglia/mailman/ganglia-general/thread/alpine.DEB.2.02.1209191232500.4417@vukovar/ +rm /var/lib/ganglia-web/conf/ganglia_metrics.cache + +Python Encrypting with PyCrypto AES +https://stackoverflow.com/questions/14179784/python-encrypting-with-pycrypto-aes + +推荐 :一文读懂最大似然估计(附R代码) +http://www.ijiandao.com/2b/baijia/171559.html + +数据科学家应知必会的6种常见概率分布 +https://blog.csdn.net/kicilove/article/details/78655856 + +通过阿里云API(php)搭建秒级DDNS动态域名解析 +https://www.zimrilink.com/share/aliddns.html + + + + +代码的好味道和坏味道之22种坏味道 +https://blog.csdn.net/zxh19800626/article/details/84781597 + +谈谈我们公司如何做Code Review +https://blog.csdn.net/zxh19800626/article/details/84995166#comments + + +office 文件预览 +https://products.office.com/zh-cn/office-online/documents-spreadsheets-presentations-office-online?rtc=1 +https://products.office.com/en-us/office-online/documents-spreadsheets-presentations-office-online?rtc=1 +libreoffice + + +ssh2,node-rdp,xterm,monaco-editor,socket.io + +请问感冒的分类有哪几种? +http://www.bu-shen.com/shen-usnyhbbn.htm + +Web code editor with Monaco code editor, JQuery file tree browser , nodejs +https://github.com/moorthi07/marscode + +Add close button to jquery ui tabs? +https://stackoverflow.com/questions/14357614/add-close-button-to-jquery-ui-tabs +http://jqueryui.com/tabs/#manipulation + +How can I get file extensions with JavaScript? +https://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript + +Linux中profile、bashrc、bash_profile之间的区别和联系 +https://blog.csdn.net/m0_37739193/article/details/72638074 + +linux中CentOS、Ubuntu、Debian三个版本系统 差别 +https://www.cnblogs.com/baichuanhuihai/p/8056976.html + +代码规范 : 表驱动法(if switch 真讨厌) +https://blog.csdn.net/qq_22555107/article/details/78884261 + +umeditor储存型xss漏洞 +https://www.yuag.org/2017/09/19/ueditor%E5%82%A8%E5%AD%98%E5%9E%8Bxss%E6%BC%8F%E6%B4%9E/ + +根据白名单过滤 HTML(防止 XSS 攻击) +https://github.com/leizongmin/js-xss/blob/master/README.zh.md + + +jQuery的.bind() .live() .delegate()和.on()之间的区别 +https://blog.csdn.net/weixin_38840741/article/details/80272203 + +Connecting to remote SSH server (via Node.js/html5 console) +https://stackoverflow.com/questions/38689707/connecting-to-remote-ssh-server-via-node-js-html5-console \ No newline at end of file From 0118bc068ee1fc41b3a143b754be32e339459326 Mon Sep 17 00:00:00 2001 From: haohu Date: Fri, 29 Mar 2019 17:59:08 +0800 Subject: [PATCH 019/137] x --- javascript/random_subject/1.html | 10 ++ javascript/random_subject/main.js | 69 +++++++++ javascript/random_subject/mockdata.js | 7 + mypost/graduation.md | 21 +++ mypost/tech_note.md | 210 +++++++++++++++++++++++++- tf/input_data.py | 31 ++++ 6 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 javascript/random_subject/1.html create mode 100644 javascript/random_subject/main.js create mode 100644 javascript/random_subject/mockdata.js create mode 100644 tf/input_data.py diff --git a/javascript/random_subject/1.html b/javascript/random_subject/1.html new file mode 100644 index 00000000..dedd8188 --- /dev/null +++ b/javascript/random_subject/1.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/javascript/random_subject/main.js b/javascript/random_subject/main.js new file mode 100644 index 00000000..554237cf --- /dev/null +++ b/javascript/random_subject/main.js @@ -0,0 +1,69 @@ +var api_url = 'http://dev.techaction.cn/api'; +var subject_types = [{type: '单选', num: 10}, {type: '多选', num: 10}]; + +var promises = []; +subject_types.forEach(function(type) { + var dfd = jQuery.Deferred(); + promises.push(dfd); + + $.getJSON(api_url+'/get_subject_list?type='+type.type, function(rsp) { + console.log('题目列表:', type.type, rsp.subjects.length, rsp.subjects); + var groups = group_by_point(rsp.subjects); + var subjects = random_subject(groups, type.num); + $.post(api_url + '/save_subjects?type=' + type.type, {subjects: subjects}, function() { + console.log('保存题型:', type); + dfd.resolve(); + }) + }) +}); + +$.when.apply($, promises).then(function() { + console.log('所有题型已保存'); +}); + +/** 随机 num 个题目, 覆盖尽量多的知识点 + * + * groups: {a: [1,2,3], b:[4,5,6]} + * num: int + * + * */ +function random_subject(groups, num) { + var ret = []; + + // 取够 ret 或取空 group + while(ret.length < num && !$.isEmptyObject(groups)) { + // 取出 point 列表, 并随机取出一个 point + var points = Object.keys(groups); // {a: [1, 2, 3], b: [4, 5, 6]} => [a, b] + var random_index = Math.floor(Math.random() * points.length); // [a, b] => 1 + var point = points[random_index]; // ([a, b], 1) => b + + // 从该 point 对应的 subjects 里随机出去一个 subject + var subjects = groups[point]; // ({a: [1, 2, 3], b: [4, 5, 6]}, b) = > [4, 5, 6] + random_index = Math.floor(Math.random() * subjects.length); // [4, 5, 6] => 2 + ret.push(subjects[random_index]); // [] => [6] + + // 移除已经取过的 subject, 以及空的 group + subjects.splice(random_index, 1); // [4, 5, 6], 2 => [4, 5] + if (subjects.length == 0) { + delete groups[point] // {a: [1, 2, 3], b: []} => {a: [1, 2, 3] } + } + } + + console.log('随机抽取题目:', num, ret.length, ret); + return ret; +} + +// 按知识点分组 +function group_by_point(list) { + var ret = {}; + list.forEach(function(item) { + var point = item.point; + if (ret[point] == undefined) { + ret[item.point] = []; + } + ret[point].push(item); + }); + + console.log('按知识点分组:', ret) + return ret; +} diff --git a/javascript/random_subject/mockdata.js b/javascript/random_subject/mockdata.js new file mode 100644 index 00000000..69292952 --- /dev/null +++ b/javascript/random_subject/mockdata.js @@ -0,0 +1,7 @@ +Mock.mock(/http:\/\/dev.techaction.cn\/api\/get_subject_list.*/, { + 'subjects|5-20': [{ + 'id': '@integer(1000, 9999)', + 'point|1': ['a', 'b', 'c', 'd'] + }] +}); +Mock.mock(/http:\/\/dev.techaction.cn\/api\/save_subjects.*/, { }); diff --git a/mypost/graduation.md b/mypost/graduation.md index ddc0fd9d..ba7c5e78 100644 --- a/mypost/graduation.md +++ b/mypost/graduation.md @@ -54,6 +54,19 @@ https://zhuanlan.zhihu.com/p/38380467 Redis 设计与实现¶ http://redisbook.com/index.html +## 字符串 + +[Redis源码阅读]sds字符串实现 +https://www.hoohack.me/2017/11/13/read-redis-src-sds +JavaScript字符串底层是如何实现的? +https://www.zhihu.com/question/51132164 +Lua中字符串的实现 +https://zhuanlan.zhihu.com/p/30757189?from_voters_page=true +C 字符串 +http://www.runoob.com/cprogramming/c-strings.html +C语言字符串操作总结大全(超详细) +https://www.cnblogs.com/lidabo/p/5225868.html + 转换成八进制数,则为 r=4, w=2, x=1, -=0(这也就是用数字设置权限时为何是4代表读,2代表写,1代表执行) @@ -106,3 +119,11 @@ r-- = 100 = 4 - Controller - Library - Helper + + +## + +- udp 模拟 tcp +- 内存分配 +- 多线程任务 +- mapreduce \ No newline at end of file diff --git a/mypost/tech_note.md b/mypost/tech_note.md index aa18ec0a..30b23495 100644 --- a/mypost/tech_note.md +++ b/mypost/tech_note.md @@ -2728,4 +2728,212 @@ jQuery的.bind() .live() .delegate()和.on()之间的区别 https://blog.csdn.net/weixin_38840741/article/details/80272203 Connecting to remote SSH server (via Node.js/html5 console) -https://stackoverflow.com/questions/38689707/connecting-to-remote-ssh-server-via-node-js-html5-console \ No newline at end of file +https://stackoverflow.com/questions/38689707/connecting-to-remote-ssh-server-via-node-js-html5-console + +C 语言经典100例 +http://www.runoob.com/cprogramming/c-100-examples.html +C 语言实例 +http://www.runoob.com/cprogramming/c-examples.html + +jqueryfiletree/jqueryfiletree +https://github.com/jqueryfiletree/jqueryfiletree + +clean-code-javascript +https://github.com/ryanmcdermott/clean-code-javascript +clean-code-php +https://github.com/jupeter/clean-code-php + + +PHP 代码简洁之道 ( PHP Clean Code) +https://zhuanlan.zhihu.com/p/33451652 + +软件开发名言:kiss, dry, 单一指责,不要过早优化,破窗理论 + +[pyp-w3] Simple Database System +https://github.com/rmotr-group-projects/pyp-w3-gw-simple-database-system +3. Practical: A Simple Database +http://www.gigamonkeys.com/book/practical-a-simple-database.html +Practical Common Lisp +http://www.gigamonkeys.com/book/ +Practical Common Lisp【个人翻译版】 +https://www.douban.com/note/178148141/ + + +需要将php.ini中的配置指令做如下修改: + +1. error_reporting = E_ALL ;将会向PHP报告发生的每个错误 + +2. display_errors = Off ;不显示满足上条 指令所定义规则的所有错误报告 + +3. log_errors = On ;决定日志语句记录的位置 + +4. log_errors_max_len = 1024 ;设置每个日志项的最大长度 + +5. error_log = /usr/local/error.log ;指定产生的 错误报告写入的日志文件位置 + +在CentOS上安装Python3的三种方法 +https://www.centos.bz/2018/01/%E5%9C%A8centos%E4%B8%8A%E5%AE%89%E8%A3%85python3%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E6%B3%95/ +How do I check whether a file exists without exceptions? +https://stackoverflow.com/questions/82831/how-do-i-check-whether-a-file-exists-without-exceptions + +logging.basicConfig(format='%(asctime)-15s %(message)s', level=logging.NOTSET, stream=sys.stdout) + +Logging, StreamHandler and standard streams +https://stackoverflow.com/questions/1383254/logging-streamhandler-and-standard-streams + +Insert an item into sorted list in Python +https://stackoverflow.com/questions/8024571/insert-an-item-into-sorted-list-in-python + +二分查找 +Inserting and removing into/from sorted list in Python +https://stackoverflow.com/questions/17348332/inserting-and-removing-into-from-sorted-list-in-python + +Python 顾问 +https://stackoverflow.com/users/100297/martijn-pieters + +跳跃表 +EFFICIENT RUNNING MEDIAN USING AN INDEXABLE SKIPLIST (PYTHON RECIPE) +http://code.activestate.com/recipes/576930-efficient-running-median-using-an-indexable-skipli/ + +Python 树 +http://interactivepython.org/runestone/static/pythonds/Trees/SearchTreeImplementation.html +https://pythonspot.com/python-tree/ + +Problem Solving with Algorithms and Data Structures using Python +http://interactivepython.org/runestone/static/pythonds/index.html +使用算法和数据结构解决问题 +https://facert.gitbooks.io/python-data-structure-cn/content/ +Learn to program, one byte at a time. +http://pythonschool.net/ + + +在Ubuntu上使用golang +https://blog.csdn.net/miracle33/article/details/82875229 + +Replace a string in shell script using a variable +https://stackoverflow.com/questions/3306007/replace-a-string-in-shell-script-using-a-variable + + +500 Lines or Less +https://github.com/aosabook/500lines +http://aosabook.org/blog/ +http://www.wangfz.com/archives/1254 + + +WLP4 Programming Language Specification +https://www.student.cs.uwaterloo.ca/~cs241/wlp4/WLP4.html + + +Ubuntu防火墙 UFW 设置 +http://www.cnblogs.com/sxwailyc/archive/2010/07/10/1774909.html + +How can I open a range of ports in ubuntu using (g)ufw +https://askubuntu.com/questions/7099/how-can-i-open-a-range-of-ports-in-ubuntu-using-gufw + +ufw allow 11200:11299/tcp +ufw allow 11200:11299/udp + +ufw allow from AAA.BBB.CCC.DDD/EE to any port 11200:11299 proto tcp +ufw allow from AAA.BBB.CCC.DDD/EE to any port 11200:11299 proto udp + + + +1700页数学笔记火了!全程敲代码,速度飞快易搜索,硬核小哥教你上手LaTeX+Vim +https://zhuanlan.zhihu.com/p/60049290 +从基本原理到梯度下降,小白都能看懂的神经网络教程 +https://zhuanlan.zhihu.com/p/59385110 + +主成分分析PCA +http://www.cnblogs.com/zhangchaoyang/articles/2222048.html +sklearn中PCA的使用方法 +https://www.jianshu.com/p/8642d5ea5389 +在线PS图片编辑器 +http://ps.xunjiepdf.com/ + + +当数据分布比较分散(即数据在平均数附近波动较大)时,各个数据与平均数的差的平方和较大,方差就较大;当数据分布比较集中时,各个数据与平均数的差的平方和较小。因此方差越大,数据的波动越大;方差越小,数据的波动就越小。 + + +Ubuntu安装Java8和Java9 +https://www.cnblogs.com/woshimrf/p/ubuntu-install-java.html + +sudo add-apt-repository ppa:webupd8team/java +sudo apt-get update +sudo apt-get install oracle-java8-installer +sudo apt install oracle-java8-set-default + +What is the Tomcat installation directory? +https://askubuntu.com/questions/135824/what-is-the-tomcat-installation-directory +Tomcat7 on Ubuntu14.04 html works but 404 error for servlets +https://stackoverflow.com/questions/32411364/tomcat7-on-ubuntu14-04-html-works-but-404-error-for-servlets + +[Machine Learning & Algorithm] 随机森林(Random Forest) +http://www.cnblogs.com/maybe2030/p/4585705.html +[Machine Learning] 机器学习常见算法分类汇总 +http://www.cnblogs.com/maybe2030/p/4665816.html#web +使用sklearn的cross_val_score进行交叉验证 +https://blog.csdn.net/qq_36523839/article/details/80707678 + +直线回归方程:当两个变量x与y之间达到显著地线性相关关系时,应用最小二乘法原理确定一条最优直线的直线方程y=a+bx,这条回归直线与个相关点的距离比任何其他直线与相关点的距离都小,是最佳的理想直线. +回归截距a:表示直线在y轴上的截距,代表直线的起点. +回归系数b:表示直线的斜率,他的实际意义是说明x每变化一个单位时,影响y平均变动的数量. +即x每增加1单位,y变化b个单位. + +https://blog.csdn.net/zxd1754771465/article/details/72896169 +由方差、偏差、噪声、泛化误差的公式可以看出,偏差度量了模型预测的期望值与真实值之间的偏离程度,刻画了模型本身的拟合能力;方差度量了训练集的变动对预测结果的影响;噪声表达了能达到的期望误差的下界,刻画了学习问题本身的难度。 + +误差(包括训练误差,测试误差)=偏差+方差+噪声 + +经验误差与泛化误差、偏差与方差、欠拟合与过拟合、交叉验证 +https://blog.csdn.net/zhihua_oba/article/details/78684257 + +偏差(Bias)与方差(Variance) +https://zhuanlan.zhihu.com/p/38853908 + +偏差,方差,训练误差,测试误差的区别 +https://blog.csdn.net/xiaopihaierletian/article/details/68921609 + + +## 机器学习 + +- 数据降维: + - PCA +- 可视化: + - 散点图:plt.scatter +- 描述性分析(describe):mean,std,value_counts +- 数据标准化(normalize):min-max,Z-score +- 划分训练集和测试集:train_test_split +- 调参: + - 网格搜索:GridSearchCV +- 模型性能评估: + - 指标:错误率,假阳性,假阴性,均方误差,残差图 + - 方法: + - 交叉验证 +- 算法: + - K 近邻:KNN + - 支持向量机:SVM + - 线性支持向量机:LinearSVC + - 随机森林:RF + - 朴素贝叶斯(NB) + - 线性回归:最小二乘,截距,回归系数 +- 特征提取 + - 文本 + - 去除停止词 + - 词袋模型,CountVectorizer + - 逆文档频率,TF-IDF,TfidfTransformer +- pipeline:vect->tfidf->svm + +查看本机对外网网卡监听的所有端口 +netstat -tnpl | awk '{print $4}' | grep -E '(0.0.0.0)|(:::)' |sed -r 's/(:::)|([0-9]+.[0-9]+.[0-9]+.[0-9]+:)//' | sort | uniq | sort -n + + + +如果你是高端人才,你更需要的是“领悟力”,而不是“学习能力” +https://www.jianshu.com/p/fbfac3358f97 + +递归下降语法分析器实现过程 +https://www.jianshu.com/p/988ce6fd0e67 + + +How can jQuery deferred be used? +https://stackoverflow.com/questions/4869609/how-can-jquery-deferred-be-used \ No newline at end of file diff --git a/tf/input_data.py b/tf/input_data.py new file mode 100644 index 00000000..fa148ae3 --- /dev/null +++ b/tf/input_data.py @@ -0,0 +1,31 @@ +# Copyright 2015 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +"""Functions for downloading and reading MNIST data.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=unused-import +import gzip +import os +import tempfile + +import numpy +from six.moves import urllib +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow as tf +from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets +# pylint: enable=unused-import From b3c76ae0a970eeede5b7b29d9bc7f70bde7dc770 Mon Sep 17 00:00:00 2001 From: huhao Date: Wed, 10 Jul 2013 18:10:19 +0800 Subject: [PATCH 020/137] First pages commit --- index.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 00000000..03f98016 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +My GitHub Page From ef242a343d70cbcedf2cd5daaec6d79da411b242 Mon Sep 17 00:00:00 2001 From: huhao Date: Wed, 10 Jul 2013 18:21:06 +0800 Subject: [PATCH 021/137] test --- angular-demo1.html | 21 + angular-demo1.js | 83 + angular.js | 14847 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 14951 insertions(+) create mode 100644 angular-demo1.html create mode 100644 angular-demo1.js create mode 100644 angular.js diff --git a/angular-demo1.html b/angular-demo1.html new file mode 100644 index 00000000..563625cc --- /dev/null +++ b/angular-demo1.html @@ -0,0 +1,21 @@ + + + + + + + AngularJS Demo + + +

+

一共有{{apple_count}}个苹果,{{orange_count}}个桔子。

+ + + + +

请输入 + 个苹果, + 个桔子 +

+ + diff --git a/angular-demo1.js b/angular-demo1.js new file mode 100644 index 00000000..088f7890 --- /dev/null +++ b/angular-demo1.js @@ -0,0 +1,83 @@ +angular.module('common', []); +angular.module('common').directive('commonErrorMsg', function(){ + return { + restrict: "A", + controller: function ($scope, $element, $attrs) { + $element.css('color', 'red'); + $scope.$on('common.showerror', function (ev, msg) { + $element.html(msg); + }); + } + } +}); + +var myApp = angular.module('myApp', ['common']); +myApp.directive('fruits', function(fruitsService) { + return { + restrict: "E", + transclude: true, + replace: true, + template: '
    ', + controller: function ($scope, $element, $attrs) { + $scope.$on('fruitsService.updated', function () { + $scope.apple_count = fruitsService.apple_count; + $scope.orange_count = fruitsService.orange_count; + }); + } + } +}) +.directive('orange', function() { + return { + restrict: "E", + template: '
  • 桔子
  • ' + } +}) +.directive('apple', function() { + return { + restrict: "E", + template: '
    ', + link: function(scope, element, attrs, $rootScope) { + scope.show = function(){ + alert('我是一个苹果'); + }; + } + } +}) +.controller('statusCtrl', function($scope, fruitsService) { + $scope.$on('fruitsService.updated', function () { + $scope.apple_count = fruitsService.apple_count; + $scope.orange_count = fruitsService.orange_count; + }); +}) +.controller('inputCtrl', function($scope, fruitsService, $rootScope) { + $scope.$watch('apple_count', function (newVal, oldVal, $scope) { + if (newVal > 10){ + $rootScope.$emit('common.showerror', 'too big'); + }else{ + fruitsService.set_apple_count(newVal); + } + }, true); + $scope.$watch('orange_count', function (newVal, oldVal, $scope) { + fruitsService.set_orange_count(newVal); + }, true); + fruitsService.set_apple_count(3); + fruitsService.set_orange_count(2); +}) +.filter('range', function() { + return function(input, total) { + total = parseInt(total); + for (var i=0; i + var values = {name: 'misko', gender: 'male'}; + var log = []; + angular.forEach(values, function(value, key){ + this.push(key + ': ' + value); + }, log); + expect(log).toEqual(['name: misko', 'gender:male']); + + * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ +function forEach(obj, iterator, context) { + var key; + if (obj) { + if (isFunction(obj)){ + for (key in obj) { + if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context); + } else if (isArrayLike(obj)) { + for (key = 0; key < obj.length; key++) + iterator.call(context, obj[key], key); + } else { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } + } + return obj; +} + +function sortedKeys(obj) { + var keys = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys.sort(); +} + +function forEachSorted(obj, iterator, context) { + var keys = sortedKeys(obj); + for ( var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) { iteratorFn(key, value) }; +} + +/** + * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric + * characters such as '012ABC'. The reason why we are not using simply a number counter is that + * the number string gets longer over time, and it can also overflow, where as the nextId + * will grow much slower, it is a string, and it will never overflow. + * + * @returns an unique alpha-numeric string + */ +function nextUid() { + var index = uid.length; + var digit; + + while(index) { + index--; + digit = uid[index].charCodeAt(0); + if (digit == 57 /*'9'*/) { + uid[index] = 'A'; + return uid.join(''); + } + if (digit == 90 /*'Z'*/) { + uid[index] = '0'; + } else { + uid[index] = String.fromCharCode(digit + 1); + return uid.join(''); + } + } + uid.unshift('0'); + return uid.join(''); +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } + else { + delete obj.$$hashKey; + } +} + +/** + * @ngdoc function + * @name angular.extend + * @function + * + * @description + * Extends the destination object `dst` by copying all of the properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + var h = dst.$$hashKey; + forEach(arguments, function(obj){ + if (obj !== dst) { + forEach(obj, function(value, key){ + dst[key] = value; + }); + } + }); + + setHashKey(dst,h); + return dst; +} + +function int(str) { + return parseInt(str, 10); +} + + +function inherit(parent, extra) { + return extend(new (extend(function() {}, {prototype:parent}))(), extra); +} + + +/** + * @ngdoc function + * @name angular.noop + * @function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. +
    +     function foo(callback) {
    +       var result = calculateResult();
    +       (callback || angular.noop)(result);
    +     }
    +   
    + */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * +
    +     function transformer(transformationFn, value) {
    +       return (transformationFn || identity)(value);
    +     };
    +   
    + */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function() {return value;};} + +/** + * @ngdoc function + * @name angular.isUndefined + * @function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value){return typeof value == 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value){return typeof value != 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value){return value != null && typeof value == 'object';} + + +/** + * @ngdoc function + * @name angular.isString + * @function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value){return typeof value == 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @function + * + * @description + * Determines if a reference is a `Number`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value){return typeof value == 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value){ + return toString.apply(value) == '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @function + * + * @description + * Determines if a reference is an `Array`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +function isArray(value) { + return toString.apply(value) == '[object Array]'; +} + + +/** + * @ngdoc function + * @name angular.isFunction + * @function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value){return typeof value == 'function';} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.document && obj.location && obj.alert && obj.setInterval; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.apply(obj) === '[object File]'; +} + + +function isBoolean(value) { + return typeof value == 'boolean'; +} + + +function trim(value) { + return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; +} + +/** + * @ngdoc function + * @name angular.isElement + * @function + * + * @description + * Determines if a reference is a DOM element (or wrapped jQuery element). + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). + */ +function isElement(node) { + return node && + (node.nodeName // we are a direct element + || (node.bind && node.find)); // we have a bind and find method part of jQuery API +} + +/** + * @param str 'key1,key2,...' + * @returns {object} in the form of {key1:true, key2:true, ...} + */ +function makeMap(str){ + var obj = {}, items = str.split(","), i; + for ( i = 0; i < items.length; i++ ) + obj[ items[i] ] = true; + return obj; +} + + +if (msie < 9) { + nodeName_ = function(element) { + element = element.nodeName ? element : element[0]; + return (element.scopeName && element.scopeName != 'HTML') + ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; + }; +} else { + nodeName_ = function(element) { + return element.nodeName ? element.nodeName : element[0].nodeName; + }; +} + + +function map(obj, iterator, context) { + var results = []; + forEach(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; +} + + +/** + * @description + * Determines the number of elements in an array, the number of properties an object has, or + * the length of a string. + * + * Note: This function is used to augment the Object type in Angular expressions. See + * {@link angular.Object} for more information about Angular arrays. + * + * @param {Object|Array|string} obj Object, array, or string to inspect. + * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object + * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. + */ +function size(obj, ownPropsOnly) { + var size = 0, key; + + if (isArray(obj) || isString(obj)) { + return obj.length; + } else if (isObject(obj)){ + for (key in obj) + if (!ownPropsOnly || obj.hasOwnProperty(key)) + size++; + } + + return size; +} + + +function includes(array, obj) { + return indexOf(array, obj) != -1; +} + +function indexOf(array, obj) { + if (array.indexOf) return array.indexOf(obj); + + for ( var i = 0; i < array.length; i++) { + if (obj === array[i]) return i; + } + return -1; +} + +function arrayRemove(array, value) { + var index = indexOf(array, value); + if (index >=0) + array.splice(index, 1); + return value; +} + +function isLeafNode (node) { + if (node) { + switch (node.nodeName) { + case "OPTION": + case "PRE": + case "TITLE": + return true; + } + } + return false; +} + +/** + * @ngdoc function + * @name angular.copy + * @function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for array) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array, `source` is returned. + * + * Note: this function is used to augment the Object type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + */ +function copy(source, destination){ + if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope"); + if (!destination) { + destination = source; + if (source) { + if (isArray(source)) { + destination = copy(source, []); + } else if (isDate(source)) { + destination = new Date(source.getTime()); + } else if (isObject(source)) { + destination = copy(source, {}); + } + } + } else { + if (source === destination) throw Error("Can't copy equivalent objects or arrays"); + if (isArray(source)) { + destination.length = 0; + for ( var i = 0; i < source.length; i++) { + destination.push(copy(source[i])); + } + } else { + var h = destination.$$hashKey; + forEach(destination, function(value, key){ + delete destination[key]; + }); + for ( var key in source) { + destination[key] = copy(source[key]); + } + setHashKey(destination,h); + } + } + return destination; +} + +/** + * Create a shallow copy of an object + */ +function shallowCopy(src, dst) { + dst = dst || {}; + + for(var key in src) { + if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') { + dst[key] = src[key]; + } + } + + return dst; +} + + +/** + * @ngdoc function + * @name angular.equals + * @function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, arrays and + * objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties pass `===` comparison. + * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal) + * + * During a property comparision, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2) { + if (t1 == 'object') { + if (isArray(o1)) { + if ((length = o1.length) == o2.length) { + for(key=0; key 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (/^\$+/.test(key)) { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @function + * + * @description + * Serializes input into a JSON-formatted string. + * + * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. + * @returns {string} Jsonified string representing `obj`. + */ +function toJson(obj, pretty) { + return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|Date|string|number} Deserialized thingy. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +function toBoolean(value) { + if (value && value.length !== 0) { + var v = lowercase("" + value); + value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); + } else { + value = false; + } + return value; +} + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone(); + try { + // turns out IE does not let you set .html() on elements which + // are not allowed to have children. So we just ignore it. + element.html(''); + } catch(e) {} + // As Per DOM Standards + var TEXT_NODE = 3; + var elemHtml = jqLite('
    ').append(element).html(); + try { + return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); + } catch(e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Parses an escaped url query string into key-value pairs. + * @returns Object.<(string|boolean)> + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}, key_value, key; + forEach((keyValue || "").split('&'), function(keyValue){ + if (keyValue) { + key_value = keyValue.split('='); + key = decodeURIComponent(key_value[0]); + obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * We need our custom method because encodeURIComponent is too agressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path + * segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); +} + + +/** + * This method is intended for encoding *key* or *value* parts of query component. We need a custom + * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be + * encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + + +/** + * @ngdoc directive + * @name ng.directive:ngApp + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * + * @description + * + * Use this directive to auto-bootstrap an application. Only + * one directive can be used per HTML document. The directive + * designates the root of the application and is typically placed + * at the root of the page. + * + * In the example below if the `ngApp` directive would not be placed + * on the `html` element then the document would not be compiled + * and the `{{ 1+2 }}` would not be resolved to `3`. + * + * `ngApp` is the easiest way to bootstrap an application. + * + + + I can add: 1 + 2 = {{ 1+2 }} + + + * + */ +function angularInit(element, bootstrap) { + var elements = [element], + appElement, + module, + names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], + NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; + + function append(element) { + element && elements.push(element); + } + + forEach(names, function(name) { + names[name] = true; + append(document.getElementById(name)); + name = name.replace(':', '\\:'); + if (element.querySelectorAll) { + forEach(element.querySelectorAll('.' + name), append); + forEach(element.querySelectorAll('.' + name + '\\:'), append); + forEach(element.querySelectorAll('[' + name + ']'), append); + } + }); + + forEach(elements, function(element) { + if (!appElement) { + var className = ' ' + element.className + ' '; + var match = NG_APP_CLASS_REGEXP.exec(className); + if (match) { + appElement = element; + module = (match[2] || '').replace(/\s+/g, ','); + } else { + forEach(element.attributes, function(attr) { + if (!appElement && names[attr.name]) { + appElement = element; + module = attr.value; + } + }); + } + } + }); + if (appElement) { + bootstrap(appElement, module ? [module] : []); + } +} + +/** + * @ngdoc function + * @name angular.bootstrap + * @description + * Use this function to manually start up angular application. + * + * See: {@link guide/bootstrap Bootstrap} + * + * @param {Element} element DOM element which is the root of angular application. + * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} + * @returns {AUTO.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules) { + var resumeBootstrapInternal = function() { + element = jqLite(element); + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + modules.unshift('ng'); + var injector = createInjector(modules); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', + function(scope, element, compile, injector) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + }] + ); + return injector; + }; + + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return resumeBootstrapInternal(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + resumeBootstrapInternal(); + }; +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator){ + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +function bindJQuery() { + // bind to jQuery if present; + jQuery = window.jQuery; + // reset to jQuery or default to us. + if (jQuery) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + controller: JQLitePrototype.controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + JQLitePatchJQueryRemove('remove', true); + JQLitePatchJQueryRemove('empty'); + JQLitePatchJQueryRemove('html'); + } else { + jqLite = JQLite; + } + angular.element = jqLite; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required")); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * @ngdoc interface + * @name angular.Module + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + return ensure(ensure(window, 'angular', Object), 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @description + * + * The `angular.module` is a global place for creating and registering Angular modules. All + * modules (angular core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * + * # Module + * + * A module is a collocation of services, directives, filters, and configuration information. Module + * is used to configure the {@link AUTO.$injector $injector}. + * + *
    +     * // Create a new module
    +     * var myModule = angular.module('myModule', []);
    +     *
    +     * // register a new service
    +     * myModule.value('appName', 'MyCoolApp');
    +     *
    +     * // configure existing services inside initialization blocks.
    +     * myModule.config(function($locationProvider) {
    +     *   // Configure existing providers
    +     *   $locationProvider.hashPrefix('!');
    +     * });
    +     * 
    + * + * Then you can create an injector and load your modules like this: + * + *
    +     * var injector = angular.injector(['ng', 'MyModule'])
    +     * 
    + * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {Array.=} requires If specified then new module is being created. If unspecified then the + * the module is being retrieved for further configuration. + * @param {Function} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw Error('No module: ' + name); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {!Array.} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke'); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _runBlocks: runBlocks, + + /** + * @ngdoc property + * @name angular.Module#requires + * @propertyOf angular.Module + * @returns {Array.} List of module names which must be loaded before this module. + * @description + * Holds the list of modules which the injector will load before the current module is loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @propertyOf angular.Module + * @returns {string} Name of the module. + * @description + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the service. + * @description + * See {@link AUTO.$provide#provider $provide.provider()}. + */ + provider: invokeLater('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link AUTO.$provide#factory $provide.factory()}. + */ + factory: invokeLater('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @methodOf angular.Module + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link AUTO.$provide#service $provide.service()}. + */ + service: invokeLater('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @methodOf angular.Module + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link AUTO.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @methodOf angular.Module + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constant are fixed, they get applied before other provide methods. + * See {@link AUTO.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @methodOf angular.Module + * @param {string} name Filter name. + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + */ + filter: invokeLater('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @methodOf angular.Module + * @param {string} name Controller name. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLater('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @methodOf angular.Module + * @param {string} name directive name + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLater('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#config + * @methodOf angular.Module + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to register work which needs to be performed on module loading. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @methodOf angular.Module + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod) { + return function() { + invokeQueue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + } + } + }); + }; + }); + +} + +/** + * @ngdoc property + * @name angular.version + * @description + * An object that contains information about the current AngularJS version. This object has the + * following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + full: '1.0.7', // all of these placeholder strings will be replaced by grunt's + major: 1, // package task + minor: 0, + dot: 7, + codeName: 'monochromatic-rainbow' +}; + + +function publishExternalAPI(angular){ + extend(angular, { + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop':noop, + 'bind':bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity':identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {counter: 0} + }); + + angularModule = setupModuleLoader(window); + try { + angularModule('ngLocale'); + } catch (e) { + angularModule('ngLocale', []).provider('$locale', $LocaleProvider); + } + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + style: styleDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCsp: ngCspDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngSubmit: ngSubmitDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngView: ngViewDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + required: requiredDirective, + ngRequired: requiredDirective, + ngValue: ngValueDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $interpolate: $InterpolateProvider, + $http: $HttpProvider, + $httpBackend: $HttpBackendProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $route: $RouteProvider, + $routeParams: $RouteParamsProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider + }); + } + ]); +} + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if + * jQuery is available, or a function that wraps the element or string in Angular's jQuery lite + * implementation (commonly referred to as jqLite). + * + * Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded` + * event fired. + * + * jqLite is a tiny, API-compatible subset of jQuery that allows + * Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality + * within a very small footprint, so only a subset of the jQuery API - methods, arguments and + * invocation styles - are supported. + * + * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never + * raw DOM references. + * + * ## Angular's jQuery lite provides the following methods: + * + * - [addClass()](http://api.jquery.com/addClass/) + * - [after()](http://api.jquery.com/after/) + * - [append()](http://api.jquery.com/append/) + * - [attr()](http://api.jquery.com/attr/) + * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces + * - [children()](http://api.jquery.com/children/) - Does not support selectors + * - [clone()](http://api.jquery.com/clone/) + * - [contents()](http://api.jquery.com/contents/) + * - [css()](http://api.jquery.com/css/) + * - [data()](http://api.jquery.com/data/) + * - [eq()](http://api.jquery.com/eq/) + * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [hasClass()](http://api.jquery.com/hasClass/) + * - [html()](http://api.jquery.com/html/) + * - [next()](http://api.jquery.com/next/) - Does not support selectors + * - [parent()](http://api.jquery.com/parent/) - Does not support selectors + * - [prepend()](http://api.jquery.com/prepend/) + * - [prop()](http://api.jquery.com/prop/) + * - [ready()](http://api.jquery.com/ready/) + * - [remove()](http://api.jquery.com/remove/) + * - [removeAttr()](http://api.jquery.com/removeAttr/) + * - [removeClass()](http://api.jquery.com/removeClass/) + * - [removeData()](http://api.jquery.com/removeData/) + * - [replaceWith()](http://api.jquery.com/replaceWith/) + * - [text()](http://api.jquery.com/text/) + * - [toggleClass()](http://api.jquery.com/toggleClass/) + * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers. + * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces + * - [val()](http://api.jquery.com/val/) + * - [wrap()](http://api.jquery.com/wrap/) + * + * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite: + * + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current + * element or its parent. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +var jqCache = JQLite.cache = {}, + jqName = JQLite.expando = 'ng-' + new Date().getTime(), + jqId = 1, + addEventListenerFn = (window.document.addEventListener + ? function(element, type, fn) {element.addEventListener(type, fn, false);} + : function(element, type, fn) {element.attachEvent('on' + type, fn);}), + removeEventListenerFn = (window.document.removeEventListener + ? function(element, type, fn) {element.removeEventListener(type, fn, false); } + : function(element, type, fn) {element.detachEvent('on' + type, fn); }); + +function jqNextId() { return ++jqId; } + + +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; + +/** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function camelCase(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +} + +///////////////////////////////////////////// +// jQuery mutation patch +// +// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a +// $destroy event on all DOM nodes being removed. +// +///////////////////////////////////////////// + +function JQLitePatchJQueryRemove(name, dispatchThis) { + var originalJqFn = jQuery.fn[name]; + originalJqFn = originalJqFn.$original || originalJqFn; + removePatch.$original = originalJqFn; + jQuery.fn[name] = removePatch; + + function removePatch() { + var list = [this], + fireEvent = dispatchThis, + set, setIndex, setLength, + element, childIndex, childLength, children, + fns, events; + + while(list.length) { + set = list.shift(); + for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { + element = jqLite(set[setIndex]); + if (fireEvent) { + element.triggerHandler('$destroy'); + } else { + fireEvent = !fireEvent; + } + for(childIndex = 0, childLength = (children = element.children()).length; + childIndex < childLength; + childIndex++) { + list.push(jQuery(children[childIndex])); + } + } + } + return originalJqFn.apply(this, arguments); + } +} + +///////////////////////////////////////////// +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + if (!(this instanceof JQLite)) { + if (isString(element) && element.charAt(0) != '<') { + throw Error('selectors not implemented'); + } + return new JQLite(element); + } + + if (isString(element)) { + var div = document.createElement('div'); + // Read about the NoScope elements here: + // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx + div.innerHTML = '
     
    ' + element; // IE insanity to make NoScope elements work! + div.removeChild(div.firstChild); // remove the superfluous div + JQLiteAddNodes(this, div.childNodes); + this.remove(); // detach the elements from the temporary DOM div. + } else { + JQLiteAddNodes(this, element); + } +} + +function JQLiteClone(element) { + return element.cloneNode(true); +} + +function JQLiteDealoc(element){ + JQLiteRemoveData(element); + for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { + JQLiteDealoc(children[i]); + } +} + +function JQLiteUnbind(element, type, fn) { + var events = JQLiteExpandoStore(element, 'events'), + handle = JQLiteExpandoStore(element, 'handle'); + + if (!handle) return; //no listeners registered + + if (isUndefined(type)) { + forEach(events, function(eventHandler, type) { + removeEventListenerFn(element, type, eventHandler); + delete events[type]; + }); + } else { + if (isUndefined(fn)) { + removeEventListenerFn(element, type, events[type]); + delete events[type]; + } else { + arrayRemove(events[type], fn); + } + } +} + +function JQLiteRemoveData(element) { + var expandoId = element[jqName], + expandoStore = jqCache[expandoId]; + + if (expandoStore) { + if (expandoStore.handle) { + expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); + JQLiteUnbind(element); + } + delete jqCache[expandoId]; + element[jqName] = undefined; // ie does not allow deletion of attributes on elements. + } +} + +function JQLiteExpandoStore(element, key, value) { + var expandoId = element[jqName], + expandoStore = jqCache[expandoId || -1]; + + if (isDefined(value)) { + if (!expandoStore) { + element[jqName] = expandoId = jqNextId(); + expandoStore = jqCache[expandoId] = {}; + } + expandoStore[key] = value; + } else { + return expandoStore && expandoStore[key]; + } +} + +function JQLiteData(element, key, value) { + var data = JQLiteExpandoStore(element, 'data'), + isSetter = isDefined(value), + keyDefined = !isSetter && isDefined(key), + isSimpleGetter = keyDefined && !isObject(key); + + if (!data && !isSimpleGetter) { + JQLiteExpandoStore(element, 'data', data = {}); + } + + if (isSetter) { + data[key] = value; + } else { + if (keyDefined) { + if (isSimpleGetter) { + // don't create data in this case. + return data && data[key]; + } else { + extend(data, key); + } + } else { + return data; + } + } +} + +function JQLiteHasClass(element, selector) { + return ((" " + element.className + " ").replace(/[\n\t]/g, " "). + indexOf( " " + selector + " " ) > -1); +} + +function JQLiteRemoveClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { + element.className = trim( + (" " + element.className + " ") + .replace(/[\n\t]/g, " ") + .replace(" " + trim(cssClass) + " ", " ") + ); + }); + } +} + +function JQLiteAddClass(element, cssClasses) { + if (cssClasses) { + forEach(cssClasses.split(' '), function(cssClass) { + if (!JQLiteHasClass(element, cssClass)) { + element.className = trim(element.className + ' ' + trim(cssClass)); + } + }); + } +} + +function JQLiteAddNodes(root, elements) { + if (elements) { + elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) + ? elements + : [ elements ]; + for(var i=0; i < elements.length; i++) { + root.push(elements[i]); + } + } +} + +function JQLiteController(element, name) { + return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); +} + +function JQLiteInheritedData(element, name, value) { + element = jqLite(element); + + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if(element[0].nodeType == 9) { + element = element.find('html'); + } + + while (element.length) { + if (value = element.data(name)) return value; + element = element.parent(); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: function(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 + // we can not use jqLite since we are not done loading and jQuery could be loaded later. + JQLite(window).bind('load', trigger); // fallback to window.onload for others + }, + toString: function() { + var value = []; + forEach(this, function(e){ value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form'.split(','), function(value) { + BOOLEAN_ELEMENTS[uppercase(value)] = true; +}); + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; +} + +forEach({ + data: JQLiteData, + inheritedData: JQLiteInheritedData, + + scope: function(element) { + return JQLiteInheritedData(element, '$scope'); + }, + + controller: JQLiteController , + + injector: function(element) { + return JQLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element,name) { + element.removeAttribute(name); + }, + + hasClass: JQLiteHasClass, + + css: function(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + var val; + + if (msie <= 8) { + // this is some IE specific weirdness that jQuery 1.6.4 does not sure why + val = element.currentStyle && element.currentStyle[name]; + if (val === '') val = 'auto'; + } + + val = val || element.style[name]; + + if (msie <= 8) { + // jquery weirdness :-/ + val = (val === '') ? undefined : val; + } + + return val; + } + }, + + attr: function(element, name, value){ + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name)|| noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: extend((msie < 9) + ? function(element, value) { + if (element.nodeType == 1 /** Element */) { + if (isUndefined(value)) + return element.innerText; + element.innerText = value; + } else { + if (isUndefined(value)) + return element.nodeValue; + element.nodeValue = value; + } + } + : function(element, value) { + if (isUndefined(value)) { + return element.textContent; + } + element.textContent = value; + }, {$dv:''}), + + val: function(element, value) { + if (isUndefined(value)) { + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { + JQLiteDealoc(childNodes[i]); + } + element.innerHTML = value; + } +}, function(fn, name){ + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + + // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for(i=0; i < this.length; i++) { + if (fn === JQLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + if (this.length) + return fn(this[0], arg1, arg2); + } + } else { + // we are a write, so apply to all children + for(i=0; i < this.length; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + return fn.$dv; + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function (event, type) { + if (!event.preventDefault) { + event.preventDefault = function() { + event.returnValue = false; //ie + }; + } + + if (!event.stopPropagation) { + event.stopPropagation = function() { + event.cancelBubble = true; //ie + }; + } + + if (!event.target) { + event.target = event.srcElement || document; + } + + if (isUndefined(event.defaultPrevented)) { + var prevent = event.preventDefault; + event.preventDefault = function() { + event.defaultPrevented = true; + prevent.call(event); + }; + event.defaultPrevented = false; + } + + event.isDefaultPrevented = function() { + return event.defaultPrevented; + }; + + forEach(events[type || event.type], function(fn) { + fn.call(element, event); + }); + + // Remove monkey-patched methods (IE), + // as they would cause memory leaks in IE8. + if (msie <= 8) { + // IE7/8 does not allow to delete property on native object + event.preventDefault = null; + event.stopPropagation = null; + event.isDefaultPrevented = null; + } else { + // It shouldn't affect normal browsers (native methods are defined on prototype). + delete event.preventDefault; + delete event.stopPropagation; + delete event.isDefaultPrevented; + } + }; + eventHandler.elem = element; + return eventHandler; +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: JQLiteRemoveData, + + dealoc: JQLiteDealoc, + + bind: function bindFn(element, type, fn){ + var events = JQLiteExpandoStore(element, 'events'), + handle = JQLiteExpandoStore(element, 'handle'); + + if (!events) JQLiteExpandoStore(element, 'events', events = {}); + if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); + + forEach(type.split(' '), function(type){ + var eventFns = events[type]; + + if (!eventFns) { + if (type == 'mouseenter' || type == 'mouseleave') { + var contains = document.body.contains || document.body.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + events[type] = []; + + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"} + bindFn(element, eventmap[type], function(event) { + var ret, target = this, related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !contains(target, related)) ){ + handle(event, type); + } + + }); + + } else { + addEventListenerFn(element, type, handle); + events[type] = []; + } + eventFns = events[type] + } + eventFns.push(fn); + }); + }, + + unbind: JQLiteUnbind, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + JQLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node){ + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element){ + if (element.nodeType === 1) + children.push(element); + }); + return children; + }, + + contents: function(element) { + return element.childNodes || []; + }, + + append: function(element, node) { + forEach(new JQLite(node), function(child){ + if (element.nodeType === 1) + element.appendChild(child); + }); + }, + + prepend: function(element, node) { + if (element.nodeType === 1) { + var index = element.firstChild; + forEach(new JQLite(node), function(child){ + if (index) { + element.insertBefore(child, index); + } else { + element.appendChild(child); + index = child; + } + }); + } + }, + + wrap: function(element, wrapNode) { + wrapNode = jqLite(wrapNode)[0]; + var parent = element.parentNode; + if (parent) { + parent.replaceChild(wrapNode, element); + } + wrapNode.appendChild(element); + }, + + remove: function(element) { + JQLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + forEach(new JQLite(newElement), function(node){ + parent.insertBefore(node, index.nextSibling); + index = node; + }); + }, + + addClass: JQLiteAddClass, + removeClass: JQLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (isUndefined(condition)) { + condition = !JQLiteHasClass(element, selector); + } + (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + + next: function(element) { + if (element.nextElementSibling) { + return element.nextElementSibling; + } + + // IE8 doesn't have nextElementSibling + var elm = element.nextSibling; + while (elm != null && elm.nodeType !== 1) { + elm = elm.nextSibling; + } + return elm; + }, + + find: function(element, selector) { + return element.getElementsByTagName(selector); + }, + + clone: JQLiteClone, + + triggerHandler: function(element, eventName) { + var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; + + forEach(eventFns, function(fn) { + fn.call(element, null); + }); + } +}, function(fn, name){ + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2) { + var value; + for(var i=0; i < this.length; i++) { + if (value == undefined) { + value = fn(this[i], arg1, arg2); + if (value !== undefined) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + JQLiteAddNodes(value, fn(this[i], arg1, arg2)); + } + } + return value == undefined ? this : value; + }; +}); + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj) { + var objType = typeof obj, + key; + + if (objType == 'object' && obj !== null) { + if (typeof (key = obj.$$hashKey) == 'function') { + // must invoke on object to keep the right this + key = obj.$$hashKey(); + } else if (key === undefined) { + key = obj.$$hashKey = nextUid(); + } + } else { + key = obj; + } + + return objType + ':' + key; +} + +/** + * HashMap which can use objects as keys + */ +function HashMap(array){ + forEach(array, this.put, this); +} +HashMap.prototype = { + /** + * Store key value pair + * @param key key to store can be any type + * @param value value to store can be any type + */ + put: function(key, value) { + this[hashKey(key)] = value; + }, + + /** + * @param key + * @returns the value for the key + */ + get: function(key) { + return this[hashKey(key)]; + }, + + /** + * Remove the key/value pair + * @param key + */ + remove: function(key) { + var value = this[key = hashKey(key)]; + delete this[key]; + return value; + } +}; + +/** + * A map where multiple values can be added to the same key such that they form a queue. + * @returns {HashQueueMap} + */ +function HashQueueMap() {} +HashQueueMap.prototype = { + /** + * Same as array push, but using an array as the value for the hash + */ + push: function(key, value) { + var array = this[key = hashKey(key)]; + if (!array) { + this[key] = [value]; + } else { + array.push(value); + } + }, + + /** + * Same as array shift, but using an array as the value for the hash + */ + shift: function(key) { + var array = this[key = hashKey(key)]; + if (array) { + if (array.length == 1) { + delete this[key]; + return array[0]; + } else { + return array.shift(); + } + } + }, + + /** + * return the first item without deleting it + */ + peek: function(key) { + var array = this[hashKey(key)]; + if (array) { + return array[0]; + } + } +}; + +/** + * @ngdoc function + * @name angular.injector + * @function + * + * @description + * Creates an injector function that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + + * @param {Array.} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @returns {function()} Injector function. See {@link AUTO.$injector $injector}. + * + * @example + * Typical usage + *
    + *   // create an injector
    + *   var $injector = angular.injector(['ng']);
    + *
    + *   // use the injector to kick off your application
    + *   // use the type inference to auto inject arguments, or use implicit injection
    + *   $injector.invoke(function($rootScope, $compile, $document){
    + *     $compile($document)($rootScope);
    + *     $rootScope.$digest();
    + *   });
    + * 
    + */ + + +/** + * @ngdoc overview + * @name AUTO + * @description + * + * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. + */ + +var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +function annotate(fn) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn == 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ + arg.replace(FN_ARG, function(all, underscore, name){ + $inject.push(name); + }); + }); + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc object + * @name AUTO.$injector + * @function + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link AUTO.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + *
    + *   var $injector = angular.injector();
    + *   expect($injector.get('$injector')).toBe($injector);
    + *   expect($injector.invoke(function($injector){
    + *     return $injector;
    + *   }).toBe($injector);
    + * 
    + * + * # Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + *
    + *   // inferred (only works if code not minified/obfuscated)
    + *   $injector.invoke(function(serviceA){});
    + *
    + *   // annotated
    + *   function explicit(serviceA) {};
    + *   explicit.$inject = ['serviceA'];
    + *   $injector.invoke(explicit);
    + *
    + *   // inline
    + *   $injector.invoke(['serviceA', function(serviceA){}]);
    + * 
    + * + * ## Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be + * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation + * tools since these tools change the argument names. + * + * ## `$inject` Annotation + * By adding a `$inject` property onto a function the injection parameters can be specified. + * + * ## Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#get + * @methodOf AUTO.$injector + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#invoke + * @methodOf AUTO.$injector + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {!function} fn The function to invoke. The function arguments come form the function annotation. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before + * the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#instantiate + * @methodOf AUTO.$injector + * @description + * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies + * all of the arguments to the constructor function as specified by the constructor annotation. + * + * @param {function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before + * the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name AUTO.$injector#annotate + * @methodOf AUTO.$injector + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is used by the injector + * to determine which services need to be injected into the function when the function is invoked. There are three + * ways in which the function can be annotated with the needed dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting + * the function into a string using `toString()` method and extracting the argument names. + *
    + *   // Given
    + *   function MyController($scope, $route) {
    + *     // ...
    + *   }
    + *
    + *   // Then
    + *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
    + * 
    + * + * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies + * are supported. + * + * # The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of + * services to be injected into the function. + *
    + *   // Given
    + *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
    + *     // ...
    + *   }
    + *   // Define function dependencies
    + *   MyController.$inject = ['$scope', '$route'];
    + *
    + *   // Then
    + *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
    + * 
    + * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very + * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives + * minification is a better choice: + * + *
    + *   // We wish to write this (not minification / obfuscation safe)
    + *   injector.invoke(function($compile, $rootScope) {
    + *     // ...
    + *   });
    + *
    + *   // We are forced to write break inlining
    + *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
    + *     // ...
    + *   };
    + *   tmpFn.$inject = ['$compile', '$rootScope'];
    + *   injector.invoke(tmpFn);
    + *
    + *   // To better support inline function the inline annotation is supported
    + *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
    + *     // ...
    + *   }]);
    + *
    + *   // Therefore
    + *   expect(injector.annotate(
    + *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
    + *    ).toEqual(['$compile', '$rootScope']);
    + * 
    + * + * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described + * above. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + + +/** + * @ngdoc object + * @name AUTO.$provide + * + * @description + * + * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance. + * The providers share the same name as the instance they create with `Provider` suffixed to them. + * + * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of + * a service. The Provider can have additional methods which would allow for configuration of the provider. + * + *
    + *   function GreetProvider() {
    + *     var salutation = 'Hello';
    + *
    + *     this.salutation = function(text) {
    + *       salutation = text;
    + *     };
    + *
    + *     this.$get = function() {
    + *       return function (name) {
    + *         return salutation + ' ' + name + '!';
    + *       };
    + *     };
    + *   }
    + *
    + *   describe('Greeter', function(){
    + *
    + *     beforeEach(module(function($provide) {
    + *       $provide.provider('greet', GreetProvider);
    + *     }));
    + *
    + *     it('should greet', inject(function(greet) {
    + *       expect(greet('angular')).toEqual('Hello angular!');
    + *     }));
    + *
    + *     it('should allow configuration of salutation', function() {
    + *       module(function(greetProvider) {
    + *         greetProvider.salutation('Ahoj');
    + *       });
    + *       inject(function(greet) {
    + *         expect(greet('angular')).toEqual('Ahoj angular!');
    + *       });
    + *     });
    + * 
    + */ + +/** + * @ngdoc method + * @name AUTO.$provide#provider + * @methodOf AUTO.$provide + * @description + * + * Register a provider for a service. The providers can be retrieved and can have additional configuration methods. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + */ + +/** + * @ngdoc method + * @name AUTO.$provide#factory + * @methodOf AUTO.$provide + * @description + * + * A short hand for configuring services if only `$get` method is required. + * + * @param {string} name The name of the instance. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for + * `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#service + * @methodOf AUTO.$provide + * @description + * + * A short hand for registering service of given class. + * + * @param {string} name The name of the instance. + * @param {Function} constructor A class (constructor function) that will be instantiated. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#value + * @methodOf AUTO.$provide + * @description + * + * A short hand for configuring services if the `$get` method is a constant. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#constant + * @methodOf AUTO.$provide + * @description + * + * A constant value, but unlike {@link AUTO.$provide#value value} it can be injected + * into configuration function (other modules) and it is not interceptable by + * {@link AUTO.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + */ + + +/** + * @ngdoc method + * @name AUTO.$provide#decorator + * @methodOf AUTO.$provide + * @description + * + * Decoration of service, allows the decorator to intercept the service instance creation. The + * returned instance may be the original instance, or a new instance which delegates to the + * original instance. + * + * @param {string} name The name of the service to decorate. + * @param {function()} decorator This function will be invoked when the service needs to be + * instantiated. The function is called using the {@link AUTO.$injector#invoke + * injector.invoke} method and is therefore fully injectable. Local injection arguments: + * + * * `$delegate` - The original service instance, which can be monkey patched, configured, + * decorated or delegated to. + */ + + +function createInjector(modulesToLoad) { + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new HashMap(), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = createInternalInjector(providerCache, function() { + throw Error("Unknown provider: " + path.join(' <- ')); + }), + instanceCache = {}, + instanceInjector = (instanceCache.$injector = + createInternalInjector(instanceCache, function(servicename) { + var provider = providerInjector.get(servicename + providerSuffix); + return instanceInjector.invoke(provider.$get, provider); + })); + + + forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + } + } + + function provider(name, provider_) { + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw Error('Provider ' + name + ' must define $get factory method.'); + } + return providerCache[name + providerSuffix] = provider_; + } + + function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, value) { return factory(name, valueFn(value)); } + + function constant(name, value) { + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad){ + var runBlocks = []; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + if (isString(module)) { + var moduleFn = angularModule(module); + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + + try { + for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + var invokeArgs = invokeQueue[i], + provider = invokeArgs[0] == '$injector' + ? providerInjector + : providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isFunction(module)) { + try { + runBlocks.push(providerInjector.invoke(module)); + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isArray(module)) { + try { + runBlocks.push(providerInjector.invoke(module)); + } catch (e) { + if (e.message) e.message += ' from ' + String(module[module.length - 1]); + throw e; + } + } else { + assertArgFn(module, 'module'); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName) { + if (typeof serviceName !== 'string') { + throw Error('Service name expected'); + } + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw Error('Circular dependency: ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + return cache[serviceName] = factory(serviceName); + } finally { + path.shift(); + } + } + } + + function invoke(fn, self, locals){ + var args = [], + $inject = annotate(fn), + length, i, + key; + + for(i = 0, length = $inject.length; i < length; i++) { + key = $inject[i]; + args.push( + locals && locals.hasOwnProperty(key) + ? locals[key] + : getService(key) + ); + } + if (!fn.$inject) { + // this means that we must be an array. + fn = fn[length]; + } + + + // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke + switch (self ? -1 : args.length) { + case 0: return fn(); + case 1: return fn(args[0]); + case 2: return fn(args[0], args[1]); + case 3: return fn(args[0], args[1], args[2]); + case 4: return fn(args[0], args[1], args[2], args[3]); + case 5: return fn(args[0], args[1], args[2], args[3], args[4]); + case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + default: return fn.apply(self, args); + } + } + + function instantiate(Type, locals) { + var Constructor = function() {}, + instance, returnedValue; + + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; + instance = new Constructor(); + returnedValue = invoke(Type, instance, locals); + + return isObject(returnedValue) ? returnedValue : instance; + } + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: annotate + }; + } +} + +/** + * @ngdoc function + * @name ng.$anchorScroll + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it checks current value of `$location.hash()` and scroll to related element, + * according to rules specified in + * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}. + * + * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor. + * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. + */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // helper function to get first anchor from a NodeList + // can't use filter.filter, as it accepts only instances of Array + // and IE can't convert NodeList to an array using [].slice + // TODO(vojta): use filter if we change it to accept lists as well + function getFirstAnchor(list) { + var result = null; + forEach(list, function(element) { + if (!result && lowercase(element.nodeName) === 'a') result = element; + }); + return result; + } + + function scroll() { + var hash = $location.hash(), elm; + + // empty hash, scroll to the top of the page + if (!hash) $window.scrollTo(0, 0); + + // element with given id + else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); + + // no element and hash == 'top', scroll to the top of the page + else if (hash === 'top') $window.scrollTo(0, 0); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction() { + $rootScope.$evalAsync(scroll); + }); + } + + return scroll; + }]; +} + +/** + * ! This is a private undocumented service ! + * + * @name ng.$browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {function()} XHR XMLHttpRequest constructor. + * @param {object} $log console.log or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + rawDocument = document[0], + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while(outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + /** + * @private + * Note: this method is used only by scenario runner + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + // force browser to execute all pollFns - this is needed so that cookies and other pollers fire + // at some deterministic time in respect to the test runner's actions. Leaving things up to the + // regular poller would result in flaky tests. + forEach(pollFns, function(pollFn){ pollFn(); }); + + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // Poll Watcher API + ////////////////////////////////////////////////////////////// + var pollFns = [], + pollTimeout; + + /** + * @name ng.$browser#addPollFn + * @methodOf ng.$browser + * + * @param {function()} fn Poll function to add + * + * @description + * Adds a function to the list of functions that poller periodically executes, + * and starts polling if not started yet. + * + * @returns {function()} the added function + */ + self.addPollFn = function(fn) { + if (isUndefined(pollTimeout)) startPoller(100, setTimeout); + pollFns.push(fn); + return fn; + }; + + /** + * @param {number} interval How often should browser call poll functions (ms) + * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. + * + * @description + * Configures the poller to run in the specified intervals, using the specified + * setTimeout fn and kicks it off. + */ + function startPoller(interval, setTimeout) { + (function check() { + forEach(pollFns, function(pollFn){ pollFn(); }); + pollTimeout = setTimeout(check, interval); + })(); + } + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var lastBrowserUrl = location.href, + baseElement = document.find('base'); + + /** + * @name ng.$browser#url + * @methodOf ng.$browser + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record ? + */ + self.url = function(url, replace) { + // setter + if (url) { + if (lastBrowserUrl == url) return; + lastBrowserUrl = url; + if ($sniffer.history) { + if (replace) history.replaceState(null, '', url); + else { + history.pushState(null, '', url); + // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 + baseElement.attr('href', baseElement.attr('href')); + } + } else { + if (replace) location.replace(url); + else location.href = url; + } + return self; + // getter + } else { + // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return location.href.replace(/%27/g,"'"); + } + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function fireUrlChange() { + if (lastBrowserUrl == self.url()) return; + + lastBrowserUrl = self.url(); + forEach(urlChangeListeners, function(listener) { + listener(self.url()); + }); + } + + /** + * @name ng.$browser#onUrlChange + * @methodOf ng.$browser + * @TODO(vojta): refactor to use node's syntax for events + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed by outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange); + // hashchange event + if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange); + // polling + else self.addPollFn(fireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * Returns current + * (always relative - without domain) + * + * @returns {string=} + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : ''; + }; + + ////////////////////////////////////////////////////////////// + // Cookies API + ////////////////////////////////////////////////////////////// + var lastCookies = {}; + var lastCookieString = ''; + var cookiePath = self.baseHref(); + + /** + * @name ng.$browser#cookies + * @methodOf ng.$browser + * + * @param {string=} name Cookie name + * @param {string=} value Cokkie value + * + * @description + * The cookies method provides a 'private' low level access to browser cookies. + * It is not meant to be used directly, use the $cookie service instead. + * + * The return values vary depending on the arguments that the method was called with as follows: + *
      + *
    • cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
    • + *
    • cookies(name, value) -> set name to value, if value is undefined delete the cookie
    • + *
    • cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
    • + *
    + * + * @returns {Object} Hash of all cookies (if called without any parameter) + */ + self.cookies = function(name, value) { + var cookieLength, cookieArray, cookie, i, index; + + if (name) { + if (value === undefined) { + rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } else { + if (isString(value)) { + cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + if (cookieLength > 4096) { + $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ + cookieLength + " > 4096 bytes)!"); + } + } + } + } else { + if (rawDocument.cookie !== lastCookieString) { + lastCookieString = rawDocument.cookie; + cookieArray = lastCookieString.split("; "); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + var name = unescape(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (lastCookies[name] === undefined) { + lastCookies[name] = unescape(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + } + }; + + + /** + * @name ng.$browser#defer + * @methodOf ng.$browser + * @param {function()} fn A function, who's execution should be defered. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchroniously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name ng.$browser#defer.cancel + * @methodOf ng.$browser.defer + * + * @description + * Cancels a defered task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +function $BrowserProvider(){ + this.$get = ['$window', '$log', '$sniffer', '$document', + function( $window, $log, $sniffer, $document){ + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc object + * @name ng.$cacheFactory + * + * @description + * Factory that constructs cache objects. + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw Error('cacheId ' + cacheId + ' taken'); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = {}, + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = {}, + freshEnd = null, + staleEnd = null; + + return caches[cacheId] = { + + put: function(key, value) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + + if (isUndefined(value)) return; + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + }, + + + get: function(key) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + + return data[key]; + }, + + + remove: function(key) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry == freshEnd) freshEnd = lruEntry.p; + if (lruEntry == staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + delete data[key]; + size--; + }, + + + removeAll: function() { + data = {}; + size = 0; + lruHash = {}; + freshEnd = staleEnd = null; + }, + + + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + info: function() { + return extend({}, stats, {size: size}); + } + }; + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry != freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd == entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry != prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc object + * @name ng.$templateCache + * + * @description + * Cache used for storing html templates. + * + * See {@link ng.$cacheFactory $cacheFactory}. + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; + + +/** + * @ngdoc function + * @name ng.$compile + * @function + * + * @description + * Compiles a piece of HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope scope} and the template together. + * + * The compilation is a process of walking the DOM tree and trying to match DOM elements to + * {@link ng.$compileProvider#directive directives}. For each match it + * executes corresponding template function and collects the + * instance functions into a single template function which is then returned. + * + * The template function can then be used once to produce the view or as it is the case with + * {@link ng.directive:ngRepeat repeater} many-times, in which + * case each call results in a view that is a DOM clone of the original template. + * + + + +
    +
    +
    +
    +
    +
    + + it('should auto compile', function() { + expect(element('div[compile]').text()).toBe('Hello Angular'); + input('html').enter('{{name}}!'); + expect(element('div[compile]').text()).toBe('Angular!'); + }); + +
    + + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives. + * @param {number} maxPriority only apply directives lower then given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as:
    `cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * Calling the linking function returns the element of the template. It is either the original element + * passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * Angular automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + *
    + *     var element = $compile('

    {{total}}

    ')(scope); + *
    + * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + *
    + *     var templateHTML = angular.element('

    {{total}}

    '), + * scope = ....; + * + * var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clone` + *
    + * + * + * For information on how the compiler works, see the + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. + */ + + +/** + * @ngdoc service + * @name ng.$compileProvider + * @function + * + * @description + */ +$CompileProvider.$inject = ['$provide']; +function $CompileProvider($provide) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, + MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ', + urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/; + + + /** + * @ngdoc function + * @name ng.$compileProvider#directive + * @methodOf ng.$compileProvider + * @function + * + * @description + * Register a new directives with the compiler. + * + * @param {string} name Name of the directive in camel-case. (ie ngBind which will match as + * ng-bind). + * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more + * info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + if (isString(name)) { + assertArg(directiveFactory, 'directive'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.name = directive.name || name; + directive.require = directive.require || (directive.controller && directive.name); + directive.restrict = directive.restrict || 'A'; + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + + /** + * @ngdoc function + * @name ng.$compileProvider#urlSanitizationWhitelist + * @methodOf ng.$compileProvider + * @function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an + * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular + * expression. If a match is found the original url is written into the dom. Otherwise the + * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.urlSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + urlSanitizationWhitelist = regexp; + return this; + } + return urlSanitizationWhitelist; + }; + + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', + '$controller', '$rootScope', '$document', + function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, + $controller, $rootScope, $document) { + + var Attributes = function(element, attr) { + this.$$element = element; + this.$attr = attr || {}; + }; + + Attributes.prototype = { + $normalize: directiveNormalize, + + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + var booleanKey = getBooleanAttrName(this.$$element[0], key), + $$observers = this.$$observers, + normalizedVal; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + + // sanitize a[href] values + if (nodeName_(this.$$element[0]) === 'A' && key === 'href') { + urlSanitizationNode.setAttribute('href', value); + + // href property always returns normalized absolute url, so we can match against that + normalizedVal = urlSanitizationNode.href; + if (!normalizedVal.match(urlSanitizationWhitelist)) { + this[key] = value = 'unsafe:' + normalizedVal; + } + } + + + if (writeAttr !== false) { + if (value === null || value === undefined) { + this.$$element.removeAttr(attrName); + } else { + this.$$element.attr(attrName, value); + } + } + + // fire observers + $$observers && forEach($$observers[key], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + }, + + + /** + * Observe an interpolated attribute. + * The observer will never be called, if given attribute is not interpolated. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(*)} fn Function that will be called whenever the attribute value changes. + * @returns {function(*)} the `fn` Function passed in. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = {})), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + return fn; + } + }; + + var urlSanitizationNode = $document[0].createElement('a'), + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }; + + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it. + $compileNodes = jqLite($compileNodes); + } + // We can not compile top level text elements since text nodes can be merged and we will + // not be able to attach scope data to them, so we will wrap them in + forEach($compileNodes, function(node, index){ + if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { + $compileNodes[index] = jqLite(node).wrap('').parent()[0]; + } + }); + var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority); + return function publicLinkFn(scope, cloneConnectFn){ + assertArg(scope, 'scope'); + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + var $linkNode = cloneConnectFn + ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! + : $compileNodes; + + // Attach scope only to non-text nodes. + for(var i = 0, ii = $linkNode.length; i + addDirective(directives, + directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority); + + // iterate over the attributes + for (var attr, name, nName, value, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + attr = nAttrs[j]; + if (attr.specified) { + name = attr.name; + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + attrs[nName] = value = trim((msie && name == 'href') + ? decodeURIComponent(node.getAttribute(name, 2)) + : attr.value); + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + addAttrInterpolateDirective(node, directives, value, nName); + addDirective(directives, nName, 'A', maxPriority); + } + } + + // use class as directive + className = node.className; + if (isString(className) && className !== '') { + while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case 3: /* Text Node */ + addTextInterpolateDirective(directives, node.nodeValue); + break; + case 8: /* Comment */ + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + break; + } + + directives.sort(byPriority); + return directives; + } + + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the + * scope argument is auto-generated to the new child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes on it. + * @returns linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) { + var terminalPriority = -Number.MAX_VALUE, + preLinkFns = [], + postLinkFns = [], + newScopeDirective = null, + newIsolateScopeDirective = null, + templateDirective = null, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + transcludeDirective, + childTranscludeFn = transcludeFn, + controllerDirectives, + linkFn, + directiveValue; + + // executes all directives on the current element + for(var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + if (directiveValue = directive.scope) { + assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode); + if (isObject(directiveValue)) { + safeAddClass($compileNode, 'ng-isolate-scope'); + newIsolateScopeDirective = directive; + } + safeAddClass($compileNode, 'ng-scope'); + newScopeDirective = newScopeDirective || directive; + } + + directiveName = directive.name; + + if (directiveValue = directive.controller) { + controllerDirectives = controllerDirectives || {}; + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); + transcludeDirective = directive; + terminalPriority = directive.priority; + if (directiveValue == 'element') { + $template = jqLite(compileNode); + $compileNode = templateAttrs.$$element = + jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); + compileNode = $compileNode[0]; + replaceWith(jqCollection, jqLite($template[0]), compileNode); + childTranscludeFn = compile($template, transcludeFn, terminalPriority); + } else { + $template = jqLite(JQLiteClone(compileNode)).contents(); + $compileNode.html(''); // clear contents + childTranscludeFn = compile($template, transcludeFn); + } + } + + if ((directiveValue = directive.template)) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + $template = jqLite('
    ' + + trim(directiveValue) + + '
    ').contents(); + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that were already applied and those that weren't + // - collect directives from the template, add them to the second group and sort them + // - append the second group with new directives to the first group + directives = directives.concat( + collectDirectives( + compileNode, + directives.splice(i + 1, directives.length - (i + 1)), + newTemplateAttrs + ) + ); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), + nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace, + childTranscludeFn); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + if (isFunction(linkFn)) { + addLinkFns(null, linkFn); + } else if (linkFn) { + addLinkFns(linkFn.pre, linkFn.post); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; + nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post) { + if (pre) { + pre.require = directive.require; + preLinkFns.push(pre); + } + if (post) { + post.require = directive.require; + postLinkFns.push(post); + } + } + + + function getControllers(require, $element) { + var value, retrievalMethod = 'data', optional = false; + if (isString(require)) { + while((value = require.charAt(0)) == '^' || value == '?') { + require = require.substr(1); + if (value == '^') { + retrievalMethod = 'inheritedData'; + } + optional = optional || value == '?'; + } + value = $element[retrievalMethod]('$' + require + 'Controller'); + if (!value && !optional) { + throw Error("No controller: " + require); + } + return value; + } else if (isArray(require)) { + value = []; + forEach(require, function(require) { + value.push(getControllers(require, $element)); + }); + } + return value; + } + + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var attrs, $element, i, ii, linkFn, controller; + + if (compileNode === linkNode) { + attrs = templateAttrs; + } else { + attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); + } + $element = attrs.$$element; + + if (newIsolateScopeDirective) { + var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/; + + var parentScope = scope.$parent || scope; + + forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) { + var match = definiton.match(LOCAL_REGEXP) || [], + attrName = match[2]|| scopeName, + mode = match[1], // @, =, or & + lastValue, + parentGet, parentSet; + + scope.$$isolateBindings[scopeName] = mode + attrName; + + switch (mode) { + + case '@': { + attrs.$observe(attrName, function(value) { + scope[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = parentScope; + break; + } + + case '=': { + parentGet = $parse(attrs[attrName]); + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = scope[scopeName] = parentGet(parentScope); + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + + ' (directive: ' + newIsolateScopeDirective.name + ')'); + }; + lastValue = scope[scopeName] = parentGet(parentScope); + scope.$watch(function parentValueWatch() { + var parentValue = parentGet(parentScope); + + if (parentValue !== scope[scopeName]) { + // we are out of sync and need to copy + if (parentValue !== lastValue) { + // parent changed and it has precedence + lastValue = scope[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(parentScope, parentValue = lastValue = scope[scopeName]); + } + } + return parentValue; + }); + break; + } + + case '&': { + parentGet = $parse(attrs[attrName]); + scope[scopeName] = function(locals) { + return parentGet(parentScope, locals); + }; + break; + } + + default: { + throw Error('Invalid isolate scope definition for directive ' + + newIsolateScopeDirective.name + ': ' + definiton); + } + } + }); + } + + if (controllerDirectives) { + forEach(controllerDirectives, function(directive) { + var locals = { + $scope: scope, + $element: $element, + $attrs: attrs, + $transclude: boundTranscludeFn + }; + + controller = directive.controller; + if (controller == '@') { + controller = attrs[directive.name]; + } + + $element.data( + '$' + directive.name + 'Controller', + $controller(controller, locals)); + }); + } + + // PRELINKING + for(i = 0, ii = preLinkFns.length; i < ii; i++) { + try { + linkFn = preLinkFns[i]; + linkFn(scope, $element, attrs, + linkFn.require && getControllers(linkFn.require, $element)); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + // RECURSION + childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn); + + // POSTLINKING + for(i = 0, ii = postLinkFns.length; i < ii; i++) { + try { + linkFn = postLinkFns[i]; + linkFn(scope, $element, attrs, + linkFn.require && getControllers(linkFn.require, $element)); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + } + } + + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority) { + var match = false; + if (hasDirectives.hasOwnProperty(name)) { + for(var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i directive.priority) && + directive.restrict.indexOf(location) != -1) { + tDirectives.push(directive); + match = true; + } + } catch(e) { $exceptionHandler(e); } + } + } + return match; + } + + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr, + $element = dst.$$element; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) != '$') { + if (src[key]) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + if (key == 'class') { + safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; + } else if (key == 'style') { + $element.attr('style', $element.attr('style') + ';' + value); + } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { + dst[key] = value; + dstAttr[key] = srcAttr[key]; + } + }); + } + + + function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, + $rootElement, replace, childTranscludeFn) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + // The fact that we have to copy and patch the directive seems wrong! + derivedSyncDirective = extend({}, origAsyncDirective, { + controller: null, templateUrl: null, transclude: null, scope: null + }); + + $compileNode.html(''); + + $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}). + success(function(content) { + var compileNode, tempTemplateAttrs, $template; + + content = denormalizeTemplate(content); + + if (replace) { + $template = jqLite('
    ' + trim(content) + '
    ').contents(); + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + collectDirectives(compileNode, directives, tempTemplateAttrs); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + + while(linkQueue.length) { + var controller = linkQueue.pop(), + linkRootElement = linkQueue.pop(), + beforeTemplateLinkNode = linkQueue.pop(), + scope = linkQueue.pop(), + linkNode = compileNode; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + // it was cloned therefore we have to clone as well. + linkNode = JQLiteClone(compileNode); + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + } + + afterTemplateNodeLinkFn(function() { + beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller); + }, scope, linkNode, $rootElement, controller); + } + linkQueue = null; + }). + error(function(response, code, headers, config) { + throw Error('Failed to load template: ' + config.url); + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { + if (linkQueue) { + linkQueue.push(scope); + linkQueue.push(node); + linkQueue.push(rootElement); + linkQueue.push(controller); + } else { + afterTemplateNodeLinkFn(function() { + beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller); + }, scope, node, rootElement, controller); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + return b.priority - a.priority; + } + + + function assertNoDuplicate(what, previousDirective, directive, element) { + if (previousDirective) { + throw Error('Multiple directives [' + previousDirective.name + ', ' + + directive.name + '] asking for ' + what + ' on: ' + startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: valueFn(function textInterpolateLinkFn(scope, node) { + var parent = node.parent(), + bindings = parent.data('$binding') || []; + bindings.push(interpolateFn); + safeAddClass(parent.data('$binding', bindings), 'ng-binding'); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }) + }); + } + } + + + function addAttrInterpolateDirective(node, directives, value, name) { + var interpolateFn = $interpolate(value, true); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + + directives.push({ + priority: 100, + compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + + if (name === 'class') { + // we need to interpolate classes again, in the case the element was replaced + // and therefore the two class attrs got merged - we want to interpolate the result + interpolateFn = $interpolate(attr[name], true); + } + + attr[name] = undefined; + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(value) { + attr.$set(name, value); + }); + }) + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell, + * but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, $element, newNode) { + var oldNode = $element[0], + parent = oldNode.parentNode, + i, ii; + + if ($rootElement) { + for(i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] == oldNode) { + $rootElement[i] = newNode; + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, oldNode); + } + + newNode[jqLite.expando] = oldNode[jqLite.expando]; + $element[0] = newNode; + } + }]; +} + +var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; +/** + * Converts all accepted directives format into proper directive name. + * All of these will become 'myDirective': + * my:DiRective + * my-directive + * x-my-directive + * data-my:directive + * + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return camelCase(name.replace(PREFIX_REGEXP, '')); +} + +/** + * @ngdoc object + * @name ng.$compile.directive.Attributes + * @description + * + * A shared object between directive compile / linking functions which contains normalized DOM element + * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed + * since all of these are treated as equivalent in Angular: + * + * + */ + +/** + * @ngdoc property + * @name ng.$compile.directive.Attributes#$attr + * @propertyOf ng.$compile.directive.Attributes + * @returns {object} A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc function + * @name ng.$compile.directive.Attributes#$set + * @methodOf ng.$compile.directive.Attributes + * @function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +/** + * @ngdoc object + * @name ng.$controllerProvider + * @description + * The {@link ng.$controller $controller service} is used by Angular to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}; + + + /** + * @ngdoc function + * @name ng.$controllerProvider#register + * @methodOf ng.$controllerProvider + * @param {string} name Controller name + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + if (isObject(name)) { + extend(controllers, name) + } else { + controllers[name] = constructor; + } + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc function + * @name ng.$controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * check `window[constructor]` on the global `window` object + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into + * a service, so that one can override this service with {@link https://gist.github.com/1649788 + * BC version}. + */ + return function(constructor, locals) { + if(isString(constructor)) { + var name = constructor; + constructor = controllers.hasOwnProperty(name) + ? controllers[name] + : getter(locals.$scope, name, true) || getter($window, name, true); + + assertArgFn(constructor, name, true); + } + + return $injector.instantiate(constructor, locals); + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$document + * @requires $window + * + * @description + * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document` + * element. + */ +function $DocumentProvider(){ + this.$get = ['$window', function(window){ + return jqLite(window.document); + }]; +} + +/** + * @ngdoc function + * @name ng.$exceptionHandler + * @requires $log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$interpolateProvider + * @function + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name ng.$interpolateProvider#startSymbol + * @methodOf ng.$interpolateProvider + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value){ + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name ng.$interpolateProvider#endSymbol + * @methodOf ng.$interpolateProvider + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value){ + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', function($parse) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length; + + /** + * @ngdoc function + * @name ng.$interpolate + * @function + * + * @requires $parse + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * +
    +         var $interpolate = ...; // injected
    +         var exp = $interpolate('Hello {{name}}!');
    +         expect(exp({name:'Angular'}).toEqual('Hello Angular!');
    +       
    + * + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @returns {function(context)} an interpolation function which is used to compute the interpolated + * string. The function has these parameters: + * + * * `context`: an object against which any expressions embedded in the strings are evaluated + * against. + * + */ + function $interpolate(text, mustHaveExpression) { + var startIndex, + endIndex, + index = 0, + parts = [], + length = text.length, + hasInterpolation = false, + fn, + exp, + concat = []; + + while(index < length) { + if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { + (index != startIndex) && parts.push(text.substring(index, startIndex)); + parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); + fn.exp = exp; + index = endIndex + endSymbolLength; + hasInterpolation = true; + } else { + // we did not find anything, so we have to add the remainder to the parts array + (index != length) && parts.push(text.substring(index)); + index = length; + } + } + + if (!(length = parts.length)) { + // we added, nothing, must have been an empty string. + parts.push(''); + length = 1; + } + + if (!mustHaveExpression || hasInterpolation) { + concat.length = length; + fn = function(context) { + for(var i = 0, ii = length, part; i html5 url + } else { + return composeProtocolHostPort(match.protocol, match.host, match.port) + + pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length); + } +} + + +function convertToHashbangUrl(url, basePath, hashPrefix) { + var match = matchUrl(url); + + // already hashbang url + if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) && + match.hash.indexOf(hashPrefix) === 0) { + return url; + // convert html5 url -> hashbang url + } else { + var search = match.search && '?' + match.search || '', + hash = match.hash && '#' + match.hash || '', + pathPrefix = pathPrefixFromBase(basePath), + path = match.path.substr(pathPrefix.length); + + if (match.path.indexOf(pathPrefix) !== 0) { + throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !'); + } + + return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath + + '#' + hashPrefix + path + search + hash; + } +} + + +/** + * LocationUrl represents an url + * This object is exposed as $location service when HTML5 mode is enabled and supported + * + * @constructor + * @param {string} url HTML5 url + * @param {string} pathPrefix + */ +function LocationUrl(url, pathPrefix, appBaseUrl) { + pathPrefix = pathPrefix || ''; + + /** + * Parse given html5 (regular) url string into properties + * @param {string} newAbsoluteUrl HTML5 url + * @private + */ + this.$$parse = function(newAbsoluteUrl) { + var match = matchUrl(newAbsoluteUrl, this); + + if (match.path.indexOf(pathPrefix) !== 0) { + throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !'); + } + + this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length)); + this.$$search = parseKeyValue(match.search); + this.$$hash = match.hash && decodeURIComponent(match.hash) || ''; + + this.$$compose(); + }; + + /** + * Compose url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) + + pathPrefix + this.$$url; + }; + + + this.$$rewriteAppUrl = function(absoluteLinkUrl) { + if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) { + return absoluteLinkUrl; + } + } + + + this.$$parse(url); +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when html5 history api is disabled or not supported + * + * @constructor + * @param {string} url Legacy url + * @param {string} hashPrefix Prefix for hash part (containing path and search) + */ +function LocationHashbangUrl(url, hashPrefix, appBaseUrl) { + var basePath; + + /** + * Parse given hashbang url into properties + * @param {string} url Hashbang url + * @private + */ + this.$$parse = function(url) { + var match = matchUrl(url, this); + + + if (match.hash && match.hash.indexOf(hashPrefix) !== 0) { + throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !'); + } + + basePath = match.path + (match.search ? '?' + match.search : ''); + match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length)); + if (match[1]) { + this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]); + } else { + this.$$path = ''; + } + + this.$$search = parseKeyValue(match[3]); + this.$$hash = match[5] && decodeURIComponent(match[5]) || ''; + + this.$$compose(); + }; + + /** + * Compose hashbang url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) + + basePath + (this.$$url ? '#' + hashPrefix + this.$$url : ''); + }; + + this.$$rewriteAppUrl = function(absoluteLinkUrl) { + if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) { + return absoluteLinkUrl; + } + } + + + this.$$parse(url); +} + + +LocationUrl.prototype = { + + /** + * Has any change been replacing ? + * @private + */ + $$replace: false, + + /** + * @ngdoc method + * @name ng.$location#absUrl + * @methodOf ng.$location + * + * @description + * This method is getter only. + * + * Return full url representation with all segments encoded according to rules specified in + * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. + * + * @return {string} full url + */ + absUrl: locationGetter('$$absUrl'), + + /** + * @ngdoc method + * @name ng.$location#url + * @methodOf ng.$location + * + * @description + * This method is getter / setter. + * + * Return url (e.g. `/path?a=b#hash`) when called without any parameter. + * + * Change path, search and hash, when called with parameter and return `$location`. + * + * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) + * @return {string} url + */ + url: function(url, replace) { + if (isUndefined(url)) + return this.$$url; + + var match = PATH_MATCH.exec(url); + if (match[1]) this.path(decodeURIComponent(match[1])); + if (match[2] || match[1]) this.search(match[3] || ''); + this.hash(match[5] || '', replace); + + return this; + }, + + /** + * @ngdoc method + * @name ng.$location#protocol + * @methodOf ng.$location + * + * @description + * This method is getter only. + * + * Return protocol of current url. + * + * @return {string} protocol of current url + */ + protocol: locationGetter('$$protocol'), + + /** + * @ngdoc method + * @name ng.$location#host + * @methodOf ng.$location + * + * @description + * This method is getter only. + * + * Return host of current url. + * + * @return {string} host of current url. + */ + host: locationGetter('$$host'), + + /** + * @ngdoc method + * @name ng.$location#port + * @methodOf ng.$location + * + * @description + * This method is getter only. + * + * Return port of current url. + * + * @return {Number} port + */ + port: locationGetter('$$port'), + + /** + * @ngdoc method + * @name ng.$location#path + * @methodOf ng.$location + * + * @description + * This method is getter / setter. + * + * Return path of current url when called without any parameter. + * + * Change path when called with parameter and return `$location`. + * + * Note: Path should always begin with forward slash (/), this method will add the forward slash + * if it is missing. + * + * @param {string=} path New path + * @return {string} path + */ + path: locationGetterSetter('$$path', function(path) { + return path.charAt(0) == '/' ? path : '/' + path; + }), + + /** + * @ngdoc method + * @name ng.$location#search + * @methodOf ng.$location + * + * @description + * This method is getter / setter. + * + * Return search part (as object) of current url when called without any parameter. + * + * Change search part when called with parameter and return `$location`. + * + * @param {string|object=} search New search params - string or hash object + * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a + * single search parameter. If the value is `null`, the parameter will be deleted. + * + * @return {string} search + */ + search: function(search, paramValue) { + if (isUndefined(search)) + return this.$$search; + + if (isDefined(paramValue)) { + if (paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } else { + this.$$search = isString(search) ? parseKeyValue(search) : search; + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name ng.$location#hash + * @methodOf ng.$location + * + * @description + * This method is getter / setter. + * + * Return hash fragment when called without any parameter. + * + * Change hash fragment when called with parameter and return `$location`. + * + * @param {string=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', identity), + + /** + * @ngdoc method + * @name ng.$location#replace + * @methodOf ng.$location + * + * @description + * If called, all changes to $location during current `$digest` will be replacing current history + * record, instead of adding new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +LocationHashbangUrl.prototype = inherit(LocationUrl.prototype); + +function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) { + LocationHashbangUrl.apply(this, arguments); + + + this.$$rewriteAppUrl = function(absoluteLinkUrl) { + if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) { + return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length); + } + } +} + +LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype); + +function locationGetter(property) { + return function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return function(value) { + if (isUndefined(value)) + return this[property]; + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc object + * @name ng.$location + * + * @requires $browser + * @requires $sniffer + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular + * Services: Using $location} + */ + +/** + * @ngdoc object + * @name ng.$locationProvider + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider(){ + var hashPrefix = '', + html5Mode = false; + + /** + * @ngdoc property + * @name ng.$locationProvider#hashPrefix + * @methodOf ng.$locationProvider + * @description + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc property + * @name ng.$locationProvider#html5Mode + * @methodOf ng.$locationProvider + * @description + * @param {string=} mode Use HTML5 strategy if available. + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isDefined(mode)) { + html5Mode = mode; + return this; + } else { + return html5Mode; + } + }; + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', + function( $rootScope, $browser, $sniffer, $rootElement) { + var $location, + basePath, + pathPrefix, + initUrl = $browser.url(), + initUrlParts = matchUrl(initUrl), + appBaseUrl; + + if (html5Mode) { + basePath = $browser.baseHref() || '/'; + pathPrefix = pathPrefixFromBase(basePath); + appBaseUrl = + composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) + + pathPrefix + '/'; + + if ($sniffer.history) { + $location = new LocationUrl( + convertToHtml5Url(initUrl, basePath, hashPrefix), + pathPrefix, appBaseUrl); + } else { + $location = new LocationHashbangInHtml5Url( + convertToHashbangUrl(initUrl, basePath, hashPrefix), + hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1)); + } + } else { + appBaseUrl = + composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) + + (initUrlParts.path || '') + + (initUrlParts.search ? ('?' + initUrlParts.search) : '') + + '#' + hashPrefix + '/'; + + $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl); + } + + $rootElement.bind('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (event.ctrlKey || event.metaKey || event.which == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (lowercase(elm[0].nodeName) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + var absHref = elm.prop('href'), + rewrittenUrl = $location.$$rewriteAppUrl(absHref); + + if (absHref && !elm.attr('target') && rewrittenUrl) { + // update location manually + $location.$$parse(rewrittenUrl); + $rootScope.$apply(); + event.preventDefault(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + window.angular['ff-684208-preventDefault'] = true; + } + }); + + + // rewrite hashbang url <> html5 url + if ($location.absUrl() != initUrl) { + $browser.url(http://github.com/$location.absUrl(), true); + } + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl) { + if ($location.absUrl() != newUrl) { + if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) { + $browser.url(http://github.com/$location.absUrl()); + return; + } + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + afterLocationChange(oldUrl); + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + } + }); + + // update browser + var changeCounter = 0; + $rootScope.$watch(function $locationWatch() { + var oldUrl = $browser.url(); + var currentReplace = $location.$$replace; + + if (!changeCounter || oldUrl != $location.absUrl()) { + changeCounter++; + $rootScope.$evalAsync(function() { + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url(http://github.com/$location.absUrl(), currentReplace); + afterLocationChange(oldUrl); + } + }); + } + $location.$$replace = false; + + return changeCounter; + }); + + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } +}]; +} + +/** + * @ngdoc object + * @name ng.$log + * @requires $window + * + * @description + * Simple service for logging. Default implementation writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * @example + + + function LogCtrl($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + } + + +
    +

    Reload this page with open console, enter text and hit the log button...

    + Message: + + + + + +
    +
    +
    + */ + +function $LogProvider(){ + this.$get = ['$window', function($window){ + return { + /** + * @ngdoc method + * @name ng.$log#log + * @methodOf ng.$log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name ng.$log#warn + * @methodOf ng.$log + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name ng.$log#info + * @methodOf ng.$log + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name ng.$log#error + * @methodOf ng.$log + * + * @description + * Write an error message + */ + error: consoleLog('error') + }; + + function formatError(arg) { + if (arg instanceof Error) { + if (arg.stack) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop; + + if (logFn.apply) { + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + return logFn.apply(console, args); + }; + } + + // we are IE which either doesn't have window.console => this is noop and we do nothing, + // or we are IE where console.log doesn't have apply so we log at least first 2 args + return function(arg1, arg2) { + logFn(arg1, arg2); + } + } + }]; +} + +var OPERATORS = { + 'null':function(){return null;}, + 'true':function(){return true;}, + 'false':function(){return false;}, + undefined:noop, + '+':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + if (isDefined(a)) { + if (isDefined(b)) { + return a + b; + } + return a; + } + return isDefined(b)?b:undefined;}, + '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, + '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, + '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, + '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, + '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, + '=':noop, + '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, + '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, + '<':function(self, locals, a,b){return a(self, locals)':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, + '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, + '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, + '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, + '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, + '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, +// '|':function(self, locals, a,b){return a|b;}, + '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, + '!':function(self, locals, a){return !a(self, locals);} +}; +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + +function lex(text, csp){ + var tokens = [], + token, + index = 0, + json = [], + ch, + lastCh = ':'; // can start regexp + + while (index < text.length) { + ch = text.charAt(index); + if (is('"\'')) { + readString(ch); + } else if (isNumber(ch) || is('.') && isNumber(peek())) { + readNumber(); + } else if (isIdent(ch)) { + readIdent(); + // identifiers can only be if the preceding char was a { or , + if (was('{,') && json[0]=='{' && + (token=tokens[tokens.length-1])) { + token.json = token.text.indexOf('.') == -1; + } + } else if (is('(){}[].,;:')) { + tokens.push({ + index:index, + text:ch, + json:(was(':[,') && is('{[')) || is('}]:,') + }); + if (is('{[')) json.unshift(ch); + if (is('}]')) json.shift(); + index++; + } else if (isWhitespace(ch)) { + index++; + continue; + } else { + var ch2 = ch + peek(), + fn = OPERATORS[ch], + fn2 = OPERATORS[ch2]; + if (fn2) { + tokens.push({index:index, text:ch2, fn:fn2}); + index += 2; + } else if (fn) { + tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); + index += 1; + } else { + throwError("Unexpected next character ", index, index+1); + } + } + lastCh = ch; + } + return tokens; + + function is(chars) { + return chars.indexOf(ch) != -1; + } + + function was(chars) { + return chars.indexOf(lastCh) != -1; + } + + function peek() { + return index + 1 < text.length ? text.charAt(index + 1) : false; + } + function isNumber(ch) { + return '0' <= ch && ch <= '9'; + } + function isWhitespace(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 + } + function isIdent(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + } + function isExpOperator(ch) { + return ch == '-' || ch == '+' || isNumber(ch); + } + + function throwError(error, start, end) { + end = end || index; + throw Error("Lexer Error: " + error + " at column" + + (isDefined(start) + ? "s " + start + "-" + index + " [" + text.substring(start, end) + "]" + : " " + end) + + " in expression [" + text + "]."); + } + + function readNumber() { + var number = ""; + var start = index; + while (index < text.length) { + var ch = lowercase(text.charAt(index)); + if (ch == '.' || isNumber(ch)) { + number += ch; + } else { + var peekCh = peek(); + if (ch == 'e' && isExpOperator(peekCh)) { + number += ch; + } else if (isExpOperator(ch) && + peekCh && isNumber(peekCh) && + number.charAt(number.length - 1) == 'e') { + number += ch; + } else if (isExpOperator(ch) && + (!peekCh || !isNumber(peekCh)) && + number.charAt(number.length - 1) == 'e') { + throwError('Invalid exponent'); + } else { + break; + } + } + index++; + } + number = 1 * number; + tokens.push({index:start, text:number, json:true, + fn:function() {return number;}}); + } + function readIdent() { + var ident = "", + start = index, + lastDot, peekIndex, methodName, ch; + + while (index < text.length) { + ch = text.charAt(index); + if (ch == '.' || isIdent(ch) || isNumber(ch)) { + if (ch == '.') lastDot = index; + ident += ch; + } else { + break; + } + index++; + } + + //check if this is not a method invocation and if it is back out to last dot + if (lastDot) { + peekIndex = index; + while(peekIndex < text.length) { + ch = text.charAt(peekIndex); + if (ch == '(') { + methodName = ident.substr(lastDot - start + 1); + ident = ident.substr(0, lastDot - start); + index = peekIndex; + break; + } + if(isWhitespace(ch)) { + peekIndex++; + } else { + break; + } + } + } + + + var token = { + index:start, + text:ident + }; + + if (OPERATORS.hasOwnProperty(ident)) { + token.fn = token.json = OPERATORS[ident]; + } else { + var getter = getterFn(ident, csp); + token.fn = extend(function(self, locals) { + return (getter(self, locals)); + }, { + assign: function(self, value) { + return setter(self, ident, value); + } + }); + } + + tokens.push(token); + + if (methodName) { + tokens.push({ + index:lastDot, + text: '.', + json: false + }); + tokens.push({ + index: lastDot + 1, + text: methodName, + json: false + }); + } + } + + function readString(quote) { + var start = index; + index++; + var string = ""; + var rawString = quote; + var escape = false; + while (index < text.length) { + var ch = text.charAt(index); + rawString += ch; + if (escape) { + if (ch == 'u') { + var hex = text.substring(index + 1, index + 5); + if (!hex.match(/[\da-f]{4}/i)) + throwError( "Invalid unicode escape [\\u" + hex + "]"); + index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + if (rep) { + string += rep; + } else { + string += ch; + } + } + escape = false; + } else if (ch == '\\') { + escape = true; + } else if (ch == quote) { + index++; + tokens.push({ + index:start, + text:rawString, + string:string, + json:true, + fn:function() { return string; } + }); + return; + } else { + string += ch; + } + index++; + } + throwError("Unterminated quote", start); + } +} + +///////////////////////////////////////// + +function parser(text, json, $filter, csp){ + var ZERO = valueFn(0), + value, + tokens = lex(text, csp), + assignment = _assignment, + functionCall = _functionCall, + fieldAccess = _fieldAccess, + objectIndex = _objectIndex, + filterChain = _filterChain; + + if(json){ + // The extra level of aliasing is here, just in case the lexer misses something, so that + // we prevent any accidental execution in JSON. + assignment = logicalOR; + functionCall = + fieldAccess = + objectIndex = + filterChain = + function() { throwError("is not valid json", {text:text, index:0}); }; + value = primary(); + } else { + value = statements(); + } + if (tokens.length !== 0) { + throwError("is an unexpected token", tokens[0]); + } + return value; + + /////////////////////////////////// + function throwError(msg, token) { + throw Error("Syntax Error: Token '" + token.text + + "' " + msg + " at column " + + (token.index + 1) + " of the expression [" + + text + "] starting at [" + text.substring(token.index) + "]."); + } + + function peekToken() { + if (tokens.length === 0) + throw Error("Unexpected end of expression: " + text); + return tokens[0]; + } + + function peek(e1, e2, e3, e4) { + if (tokens.length > 0) { + var token = tokens[0]; + var t = token.text; + if (t==e1 || t==e2 || t==e3 || t==e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + } + + function expect(e1, e2, e3, e4){ + var token = peek(e1, e2, e3, e4); + if (token) { + if (json && !token.json) { + throwError("is not valid json", token); + } + tokens.shift(); + return token; + } + return false; + } + + function consume(e1){ + if (!expect(e1)) { + throwError("is unexpected, expecting [" + e1 + "]", peek()); + } + } + + function unaryFn(fn, right) { + return function(self, locals) { + return fn(self, locals, right); + }; + } + + function binaryFn(left, fn, right) { + return function(self, locals) { + return fn(self, locals, left, right); + }; + } + + function statements() { + var statements = []; + while(true) { + if (tokens.length > 0 && !peek('}', ')', ';', ']')) + statements.push(filterChain()); + if (!expect(';')) { + // optimize for the common case where there is only one statement. + // TODO(size): maybe we should not support multiple statements? + return statements.length == 1 + ? statements[0] + : function(self, locals){ + var value; + for ( var i = 0; i < statements.length; i++) { + var statement = statements[i]; + if (statement) + value = statement(self, locals); + } + return value; + }; + } + } + } + + function _filterChain() { + var left = expression(); + var token; + while(true) { + if ((token = expect('|'))) { + left = binaryFn(left, token.fn, filter()); + } else { + return left; + } + } + } + + function filter() { + var token = expect(); + var fn = $filter(token.text); + var argsFn = []; + while(true) { + if ((token = expect(':'))) { + argsFn.push(expression()); + } else { + var fnInvoke = function(self, locals, input){ + var args = [input]; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self, locals)); + } + return fn.apply(self, args); + }; + return function() { + return fnInvoke; + }; + } + } + } + + function expression() { + return assignment(); + } + + function _assignment() { + var left = logicalOR(); + var right; + var token; + if ((token = expect('='))) { + if (!left.assign) { + throwError("implies assignment but [" + + text.substring(0, token.index) + "] can not be assigned to", token); + } + right = logicalOR(); + return function(scope, locals){ + return left.assign(scope, right(scope, locals), locals); + }; + } else { + return left; + } + } + + function logicalOR() { + var left = logicalAND(); + var token; + while(true) { + if ((token = expect('||'))) { + left = binaryFn(left, token.fn, logicalAND()); + } else { + return left; + } + } + } + + function logicalAND() { + var left = equality(); + var token; + if ((token = expect('&&'))) { + left = binaryFn(left, token.fn, logicalAND()); + } + return left; + } + + function equality() { + var left = relational(); + var token; + if ((token = expect('==','!='))) { + left = binaryFn(left, token.fn, equality()); + } + return left; + } + + function relational() { + var left = additive(); + var token; + if ((token = expect('<', '>', '<=', '>='))) { + left = binaryFn(left, token.fn, relational()); + } + return left; + } + + function additive() { + var left = multiplicative(); + var token; + while ((token = expect('+','-'))) { + left = binaryFn(left, token.fn, multiplicative()); + } + return left; + } + + function multiplicative() { + var left = unary(); + var token; + while ((token = expect('*','/','%'))) { + left = binaryFn(left, token.fn, unary()); + } + return left; + } + + function unary() { + var token; + if (expect('+')) { + return primary(); + } else if ((token = expect('-'))) { + return binaryFn(ZERO, token.fn, unary()); + } else if ((token = expect('!'))) { + return unaryFn(token.fn, unary()); + } else { + return primary(); + } + } + + + function primary() { + var primary; + if (expect('(')) { + primary = filterChain(); + consume(')'); + } else if (expect('[')) { + primary = arrayDeclaration(); + } else if (expect('{')) { + primary = object(); + } else { + var token = expect(); + primary = token.fn; + if (!primary) { + throwError("not a primary expression", token); + } + } + + var next, context; + while ((next = expect('(', '[', '.'))) { + if (next.text === '(') { + primary = functionCall(primary, context); + context = null; + } else if (next.text === '[') { + context = primary; + primary = objectIndex(primary); + } else if (next.text === '.') { + context = primary; + primary = fieldAccess(primary); + } else { + throwError("IMPOSSIBLE"); + } + } + return primary; + } + + function _fieldAccess(object) { + var field = expect().text; + var getter = getterFn(field, csp); + return extend( + function(scope, locals, self) { + return getter(self || object(scope, locals), locals); + }, + { + assign:function(scope, value, locals) { + return setter(object(scope, locals), field, value); + } + } + ); + } + + function _objectIndex(obj) { + var indexFn = expression(); + consume(']'); + return extend( + function(self, locals){ + var o = obj(self, locals), + i = indexFn(self, locals), + v, p; + + if (!o) return undefined; + v = o[i]; + if (v && v.then) { + p = v; + if (!('$$v' in v)) { + p.$$v = undefined; + p.then(function(val) { p.$$v = val; }); + } + v = v.$$v; + } + return v; + }, { + assign:function(self, value, locals){ + return obj(self, locals)[indexFn(self, locals)] = value; + } + }); + } + + function _functionCall(fn, contextGetter) { + var argsFn = []; + if (peekToken().text != ')') { + do { + argsFn.push(expression()); + } while (expect(',')); + } + consume(')'); + return function(scope, locals){ + var args = [], + context = contextGetter ? contextGetter(scope, locals) : scope; + + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](scope, locals)); + } + var fnPtr = fn(scope, locals, context) || noop; + // IE stupidity! + return fnPtr.apply + ? fnPtr.apply(context, args) + : fnPtr(args[0], args[1], args[2], args[3], args[4]); + }; + } + + // This is used with json array declaration + function arrayDeclaration () { + var elementFns = []; + if (peekToken().text != ']') { + do { + elementFns.push(expression()); + } while (expect(',')); + } + consume(']'); + return function(self, locals){ + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self, locals)); + } + return array; + }; + } + + function object () { + var keyValues = []; + if (peekToken().text != '}') { + do { + var token = expect(), + key = token.string || token.text; + consume(":"); + var value = expression(); + keyValues.push({key:key, value:value}); + } while (expect(',')); + } + consume('}'); + return function(self, locals){ + var object = {}; + for ( var i = 0; i < keyValues.length; i++) { + var keyValue = keyValues[i]; + object[keyValue.key] = keyValue.value(self, locals); + } + return object; + }; + } +} + +////////////////////////////////////////////////// +// Parser helper functions +////////////////////////////////////////////////// + +function setter(obj, path, setValue) { + var element = path.split('.'); + for (var i = 0; element.length > 1; i++) { + var key = element.shift(); + var propertyObj = obj[key]; + if (!propertyObj) { + propertyObj = {}; + obj[key] = propertyObj; + } + obj = propertyObj; + } + obj[element.shift()] = setValue; + return setValue; +} + +/** + * Return the value accesible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {string} path path to traverse + * @param {boolean=true} bindFnToScope + * @returns value as accesbile by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +var getterFnCache = {}; + +/** + * Implementation of the "Black Hole" variant from: + * - http://jsperf.com/angularjs-parse-getter/4 + * - http://jsperf.com/path-evaluation-simplified/7 + */ +function cspSafeGetterFn(key0, key1, key2, key3, key4) { + return function(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, + promise; + + if (pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key0]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key1 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key1]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key2 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key2]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key3 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key3]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + if (!key4 || pathVal === null || pathVal === undefined) return pathVal; + + pathVal = pathVal[key4]; + if (pathVal && pathVal.then) { + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + return pathVal; + }; +} + +function getterFn(path, csp) { + if (getterFnCache.hasOwnProperty(path)) { + return getterFnCache[path]; + } + + var pathKeys = path.split('.'), + pathKeysLength = pathKeys.length, + fn; + + if (csp) { + fn = (pathKeysLength < 6) + ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) + : function(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn( + pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] + )(scope, locals); + + locals = undefined; // clear after first iteration + scope = val; + } while (i < pathKeysLength); + return val; + } + } else { + var code = 'var l, fn, p;\n'; + forEach(pathKeys, function(key, index) { + code += 'if(s === null || s === undefined) return s;\n' + + 'l=s;\n' + + 's='+ (index + // we simply dereference 's' on any .dot notation + ? 's' + // but if we are first then we check locals first, and if so read it first + : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + + 'if (s && s.then) {\n' + + ' if (!("$$v" in s)) {\n' + + ' p=s;\n' + + ' p.$$v = undefined;\n' + + ' p.then(function(v) {p.$$v=v;});\n' + + '}\n' + + ' s=s.$$v\n' + + '}\n'; + }); + code += 'return s;'; + fn = Function('s', 'k', code); // s=scope, k=locals + fn.toString = function() { return code; }; + } + + return getterFnCache[path] = fn; +} + +/////////////////////////////////// + +/** + * @ngdoc function + * @name ng.$parse + * @function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + *
    + *   var getter = $parse('user.name');
    + *   var setter = getter.assign;
    + *   var context = {user:{name:'angular'}};
    + *   var locals = {user:{name:'local'}};
    + *
    + *   expect(getter(context)).toEqual('angular');
    + *   setter(context, 'newValue');
    + *   expect(context.user.name).toEqual('newValue');
    + *   expect(getter(context, locals)).toEqual('local');
    + * 
    + * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (tipically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The return function also has an `assign` property, if the expression is assignable, which + * allows one to set values to expressions. + * + */ +function $ParseProvider() { + var cache = {}; + this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { + return function(exp) { + switch(typeof exp) { + case 'string': + return cache.hasOwnProperty(exp) + ? cache[exp] + : cache[exp] = parser(exp, false, $filter, $sniffer.csp); + case 'function': + return exp; + default: + return noop; + } + }; + }]; +} + +/** + * @ngdoc service + * @name ng.$q + * @requires $rootScope + * + * @description + * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + *
    + *   // for the purpose of this example let's assume that variables `$q` and `scope` are
    + *   // available in the current lexical scope (they could have been injected or passed in).
    + *
    + *   function asyncGreet(name) {
    + *     var deferred = $q.defer();
    + *
    + *     setTimeout(function() {
    + *       // since this fn executes async in a future turn of the event loop, we need to wrap
    + *       // our code into an $apply call so that the model changes are properly observed.
    + *       scope.$apply(function() {
    + *         if (okToGreet(name)) {
    + *           deferred.resolve('Hello, ' + name + '!');
    + *         } else {
    + *           deferred.reject('Greeting ' + name + ' is not allowed.');
    + *         }
    + *       });
    + *     }, 1000);
    + *
    + *     return deferred.promise;
    + *   }
    + *
    + *   var promise = asyncGreet('Robin Hood');
    + *   promise.then(function(greeting) {
    + *     alert('Success: ' + greeting);
    + *   }, function(reason) {
    + *     alert('Failed: ' + reason);
    + *   });
    + * 
    + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of + * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved + * or rejected calls one of the success or error callbacks asynchronously as soon as the result + * is available. The callbacks are called with a single argument the result or rejection reason. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback` or `errorCallback`. + * + * + * # Chaining promises + * + * Because calling `then` api of a promise returns a new derived promise, it is easily possible + * to create a chain of promises: + * + *
    + *   promiseB = promiseA.then(function(result) {
    + *     return result + 1;
    + *   });
    + *
    + *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
    + *   // the result of promiseA incremented by 1
    + * 
    + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful apis like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + * There are three main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in angular, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - $q promises are recognized by the templating engine in angular, which means that in templates + * you can treat promises attached to a scope as if they were the resulting values. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * # Testing + * + *
    + *    it('should simulate promise', inject(function($q, $rootScope) {
    + *      var deferred = $q.defer();
    + *      var promise = deferred.promise;
    + *      var resolvedValue;
    + * 
    + *      promise.then(function(value) { resolvedValue = value; });
    + *      expect(resolvedValue).toBeUndefined();
    + * 
    + *      // Simulate resolving of promise
    + *      deferred.resolve(123);
    + *      // Note that the 'then' function does not get called synchronously.
    + *      // This is because we want the promise API to always be async, whether or not
    + *      // it got called synchronously or asynchronously.
    + *      expect(resolvedValue).toBeUndefined();
    + * 
    + *      // Propagate promise resolution to 'then' functions using $apply().
    + *      $rootScope.$apply();
    + *      expect(resolvedValue).toEqual(123);
    + *    });
    + *  
    + */ +function $QProvider() { + + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler); + }]; +} + + +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) { + + /** + * @ngdoc + * @name ng.$q#defer + * @methodOf ng.$q + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + var defer = function() { + var pending = [], + value, deferred; + + deferred = { + + resolve: function(val) { + if (pending) { + var callbacks = pending; + pending = undefined; + value = ref(val); + + if (callbacks.length) { + nextTick(function() { + var callback; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callback = callbacks[i]; + value.then(callback[0], callback[1]); + } + }); + } + } + }, + + + reject: function(reason) { + deferred.resolve(reject(reason)); + }, + + + promise: { + then: function(callback, errback) { + var result = defer(); + + var wrappedCallback = function(value) { + try { + result.resolve((callback || defaultCallback)(value)); + } catch(e) { + exceptionHandler(e); + result.reject(e); + } + }; + + var wrappedErrback = function(reason) { + try { + result.resolve((errback || defaultErrback)(reason)); + } catch(e) { + exceptionHandler(e); + result.reject(e); + } + }; + + if (pending) { + pending.push([wrappedCallback, wrappedErrback]); + } else { + value.then(wrappedCallback, wrappedErrback); + } + + return result.promise; + } + } + }; + + return deferred; + }; + + + var ref = function(value) { + if (value && value.then) return value; + return { + then: function(callback) { + var result = defer(); + nextTick(function() { + result.resolve(callback(value)); + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc + * @name ng.$q#reject + * @methodOf ng.$q + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + *
    +   *   promiseB = promiseA.then(function(result) {
    +   *     // success: do something and resolve promiseB
    +   *     //          with the old or a new result
    +   *     return result;
    +   *   }, function(reason) {
    +   *     // error: handle the error if possible and
    +   *     //        resolve promiseB with newPromiseOrValue,
    +   *     //        otherwise forward the rejection to promiseB
    +   *     if (canHandle(reason)) {
    +   *      // handle the error and recover
    +   *      return newPromiseOrValue;
    +   *     }
    +   *     return $q.reject(reason);
    +   *   });
    +   * 
    + * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + var reject = function(reason) { + return { + then: function(callback, errback) { + var result = defer(); + nextTick(function() { + result.resolve((errback || defaultErrback)(reason)); + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc + * @name ng.$q#when + * @methodOf ng.$q + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @returns {Promise} Returns a promise of the passed value or promise + */ + var when = function(value, callback, errback) { + var result = defer(), + done; + + var wrappedCallback = function(value) { + try { + return (callback || defaultCallback)(value); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + var wrappedErrback = function(reason) { + try { + return (errback || defaultErrback)(reason); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + nextTick(function() { + ref(value).then(function(value) { + if (done) return; + done = true; + result.resolve(ref(value).then(wrappedCallback, wrappedErrback)); + }, function(reason) { + if (done) return; + done = true; + result.resolve(wrappedErrback(reason)); + }); + }); + + return result.promise; + }; + + + function defaultCallback(value) { + return value; + } + + + function defaultErrback(reason) { + return reject(reason); + } + + + /** + * @ngdoc + * @name ng.$q#all + * @methodOf ng.$q + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.} promises An array of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array of values, + * each value corresponding to the promise at the same index in the `promises` array. If any of + * the promises is resolved with a rejection, this resulting promise will be resolved with the + * same rejection. + */ + function all(promises) { + var deferred = defer(), + counter = promises.length, + results = []; + + if (counter) { + forEach(promises, function(promise, index) { + ref(promise).then(function(value) { + if (index in results) return; + results[index] = value; + if (!(--counter)) deferred.resolve(results); + }, function(reason) { + if (index in results) return; + deferred.reject(reason); + }); + }); + } else { + deferred.resolve(results); + } + + return deferred.promise; + } + + return { + defer: defer, + reject: reject, + when: when, + all: all + }; +} + +/** + * @ngdoc object + * @name ng.$routeProvider + * @function + * + * @description + * + * Used for configuring routes. See {@link ng.$route $route} for an example. + */ +function $RouteProvider(){ + var routes = {}; + + /** + * @ngdoc method + * @name ng.$routeProvider#when + * @methodOf ng.$routeProvider + * + * @param {string} path Route path (matched against `$location.path`). If `$location.path` + * contains redundant trailing slash or is missing one, the route will still match and the + * `$location.path` will be updated to add or drop the trailing slash to exactly match the + * route definition. + * + * `path` can contain named groups starting with a colon (`:name`). All characters up to the + * next slash are matched and stored in `$routeParams` under the given `name` when the route + * matches. + * + * @param {Object} route Mapping information to be assigned to `$route.current` on route + * match. + * + * Object properties: + * + * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly + * created scope or the name of a {@link angular.Module#controller registered controller} + * if passed as a string. + * - `template` – `{string=}` – html template as a string that should be used by + * {@link ng.directive:ngView ngView} or + * {@link ng.directive:ngInclude ngInclude} directives. + * this property takes precedence over `templateUrl`. + * - `templateUrl` – `{string=}` – path to an html template that should be used by + * {@link ng.directive:ngView ngView}. + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, they will be + * resolved and converted to a value before the controller is instantiated and the + * `$routeChangeSuccess` event is fired. The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is resolved + * before its value is injected into the controller. + * + * - `redirectTo` – {(string|function())=} – value to update + * {@link ng.$location $location} path with and trigger route redirection. + * + * If `redirectTo` is a function, it will be called with the following parameters: + * + * - `{Object.}` - route parameters extracted from the current + * `$location.path()` by applying the current route templateUrl. + * - `{string}` - current `$location.path()` + * - `{Object}` - current `$location.search()` + * + * The custom `redirectTo` function is expected to return a string which will be used + * to update `$location.path()` and `$location.search()`. + * + * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() + * changes. + * + * If the option is set to `false` and url in the browser changes, then + * `$routeUpdate` event is broadcasted on the root scope. + * + * @returns {Object} self + * + * @description + * Adds a new route definition to the `$route` service. + */ + this.when = function(path, route) { + routes[path] = extend({reloadOnSearch: true}, route); + + // create redirection for trailing slashes + if (path) { + var redirectPath = (path[path.length-1] == '/') + ? path.substr(0, path.length-1) + : path +'/'; + + routes[redirectPath] = {redirectTo: path}; + } + + return this; + }; + + /** + * @ngdoc method + * @name ng.$routeProvider#otherwise + * @methodOf ng.$routeProvider + * + * @description + * Sets route definition that will be used on route change when no other route definition + * is matched. + * + * @param {Object} params Mapping information to be assigned to `$route.current`. + * @returns {Object} self + */ + this.otherwise = function(params) { + this.when(null, params); + return this; + }; + + + this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', + function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { + + /** + * @ngdoc object + * @name ng.$route + * @requires $location + * @requires $routeParams + * + * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as define in route definition. + * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * + * @property {Array.} routes Array of all configured routes. + * + * @description + * Is used for deep-linking URLs to controllers and views (HTML partials). + * It watches `$location.url()` and tries to map the path to an existing route definition. + * + * You can define routes through {@link ng.$routeProvider $routeProvider}'s API. + * + * The `$route` service is typically used in conjunction with {@link ng.directive:ngView ngView} + * directive and the {@link ng.$routeParams $routeParams} service. + * + * @example + This example shows how changing the URL hash causes the `$route` to match a route against the + URL, and the `ngView` pulls in the partial. + + Note that this example is using {@link ng.directive:script inlined templates} + to get it working on jsfiddle as well. + + + +
    + Choose: + Moby | + Moby: Ch1 | + Gatsby | + Gatsby: Ch4 | + Scarlet Letter
    + +
    +
    + +
    $location.path() = {{$location.path()}}
    +
    $route.current.templateUrl = {{$route.current.templateUrl}}
    +
    $route.current.params = {{$route.current.params}}
    +
    $route.current.scope.name = {{$route.current.scope.name}}
    +
    $routeParams = {{$routeParams}}
    +
    +
    + + + controller: {{name}}
    + Book Id: {{params.bookId}}
    +
    + + + controller: {{name}}
    + Book Id: {{params.bookId}}
    + Chapter Id: {{params.chapterId}} +
    + + + angular.module('ngView', [], function($routeProvider, $locationProvider) { + $routeProvider.when('/Book/:bookId', { + templateUrl: 'book.html', + controller: BookCntl, + resolve: { + // I will cause a 1 second delay + delay: function($q, $timeout) { + var delay = $q.defer(); + $timeout(delay.resolve, 1000); + return delay.promise; + } + } + }); + $routeProvider.when('/Book/:bookId/ch/:chapterId', { + templateUrl: 'chapter.html', + controller: ChapterCntl + }); + + // configure html5 to get links working on jsfiddle + $locationProvider.html5Mode(true); + }); + + function MainCntl($scope, $route, $routeParams, $location) { + $scope.$route = $route; + $scope.$location = $location; + $scope.$routeParams = $routeParams; + } + + function BookCntl($scope, $routeParams) { + $scope.name = "BookCntl"; + $scope.params = $routeParams; + } + + function ChapterCntl($scope, $routeParams) { + $scope.name = "ChapterCntl"; + $scope.params = $routeParams; + } + + + + it('should load and compile correct template', function() { + element('a:contains("Moby: Ch1")').click(); + var content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: ChapterCntl/); + expect(content).toMatch(/Book Id\: Moby/); + expect(content).toMatch(/Chapter Id\: 1/); + + element('a:contains("Scarlet")').click(); + sleep(2); // promises are not part of scenario waiting + content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: BookCntl/); + expect(content).toMatch(/Book Id\: Scarlet/); + }); + +
    + */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeStart + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occurs. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$routeChangeSuccess` is fired. + * + * @param {Route} next Future route information. + * @param {Route} current Current route information. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeSuccess + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted after a route dependencies are resolved. + * {@link ng.directive:ngView ngView} listens for the directive + * to instantiate the controller and render the view. + * + * @param {Object} angularEvent Synthetic event object. + * @param {Route} current Current route information. + * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeChangeError + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * Broadcasted if any of the resolve promises are rejected. + * + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. + */ + + /** + * @ngdoc event + * @name ng.$route#$routeUpdate + * @eventOf ng.$route + * @eventType broadcast on root scope + * @description + * + * The `reloadOnSearch` property has been set to false, and we are reusing the same + * instance of the Controller. + */ + + var forceReload = false, + $route = { + routes: routes, + + /** + * @ngdoc method + * @name ng.$route#reload + * @methodOf ng.$route + * + * @description + * Causes `$route` service to reload the current route even if + * {@link ng.$location $location} hasn't changed. + * + * As a result of that, {@link ng.directive:ngView ngView} + * creates new scope, reinstantiates the controller. + */ + reload: function() { + forceReload = true; + $rootScope.$evalAsync(updateRoute); + } + }; + + $rootScope.$on('$locationChangeSuccess', updateRoute); + + return $route; + + ///////////////////////////////////////////////////// + + /** + * @param on {string} current url + * @param when {string} route when template to match the url against + * @return {?Object} + */ + function switchRouteMatcher(on, when) { + // TODO(i): this code is convoluted and inefficient, we should construct the route matching + // regex only once and then reuse it + + // Escape regexp special characters. + when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$'; + var regex = '', + params = [], + dst = {}; + + var re = /:(\w+)/g, + paramMatch, + lastMatchedIndex = 0; + + while ((paramMatch = re.exec(when)) !== null) { + // Find each :param in `when` and replace it with a capturing group. + // Append all other sections of when unchanged. + regex += when.slice(lastMatchedIndex, paramMatch.index); + regex += '([^\\/]*)'; + params.push(paramMatch[1]); + lastMatchedIndex = re.lastIndex; + } + // Append trailing path part. + regex += when.substr(lastMatchedIndex); + + var match = on.match(new RegExp(regex)); + if (match) { + forEach(params, function(name, index) { + dst[name] = match[index + 1]; + }); + } + return match ? dst : null; + } + + function updateRoute() { + var next = parseRoute(), + last = $route.current; + + if (next && last && next.$$route === last.$$route + && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) { + last.params = next.params; + copy(last.params, $routeParams); + $rootScope.$broadcast('$routeUpdate', last); + } else if (next || last) { + forceReload = false; + $rootScope.$broadcast('$routeChangeStart', next, last); + $route.current = next; + if (next) { + if (next.redirectTo) { + if (isString(next.redirectTo)) { + $location.path(interpolate(next.redirectTo, next.params)).search(next.params) + .replace(); + } else { + $location.url(http://github.com/next.redirectTo(next.pathParams,%20$location.path(), $location.search())) + .replace(); + } + } + } + + $q.when(next). + then(function() { + if (next) { + var keys = [], + values = [], + template; + + forEach(next.resolve || {}, function(value, key) { + keys.push(key); + values.push(isString(value) ? $injector.get(value) : $injector.invoke(value)); + }); + if (isDefined(template = next.template)) { + } else if (isDefined(template = next.templateUrl)) { + template = $http.get(template, {cache: $templateCache}). + then(function(response) { return response.data; }); + } + if (isDefined(template)) { + keys.push('$template'); + values.push(template); + } + return $q.all(values).then(function(values) { + var locals = {}; + forEach(values, function(value, index) { + locals[keys[index]] = value; + }); + return locals; + }); + } + }). + // after route change + then(function(locals) { + if (next == $route.current) { + if (next) { + next.locals = locals; + copy(next.params, $routeParams); + } + $rootScope.$broadcast('$routeChangeSuccess', next, last); + } + }, function(error) { + if (next == $route.current) { + $rootScope.$broadcast('$routeChangeError', next, last, error); + } + }); + } + } + + + /** + * @returns the current active route, by matching it against the URL + */ + function parseRoute() { + // Match a route + var params, match; + forEach(routes, function(route, path) { + if (!match && (params = switchRouteMatcher($location.path(), path))) { + match = inherit(route, { + params: extend({}, $location.search(), params), + pathParams: params}); + match.$$route = route; + } + }); + // No route matched; fallback to "otherwise" route + return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); + } + + /** + * @returns interpolation of the redirect path with the parametrs + */ + function interpolate(string, params) { + var result = []; + forEach((string||'').split(':'), function(segment, i) { + if (i == 0) { + result.push(segment); + } else { + var segmentMatch = segment.match(/(\w+)(.*)/); + var key = segmentMatch[1]; + result.push(params[key]); + result.push(segmentMatch[2] || ''); + delete params[key]; + } + }); + return result.join(''); + } + }]; +} + +/** + * @ngdoc object + * @name ng.$routeParams + * @requires $route + * + * @description + * Current set of route parameters. The route parameters are a combination of the + * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters + * are extracted when the {@link ng.$route $route} path is matched. + * + * In case of parameter name collision, `path` params take precedence over `search` params. + * + * The service guarantees that the identity of the `$routeParams` object will remain unchanged + * (but its properties will likely change) even when a route change occurs. + * + * @example + *
    + *  // Given:
    + *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
    + *  // Route: /Chapter/:chapterId/Section/:sectionId
    + *  //
    + *  // Then
    + *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
    + * 
    + */ +function $RouteParamsProvider() { + this.$get = valueFn({}); +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - this means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (shift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in middle are expensive so we use linked list + * + * There are few watches then a lot of observers. This is why you don't want the observer to be + * implemented in the same way as watch. Watch requires return of initialization function which + * are expensive to construct. + */ + + +/** + * @ngdoc object + * @name ng.$rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc function + * @name ng.$rootScopeProvider#digestTtl + * @methodOf ng.$rootScopeProvider + * @description + * + * Sets the number of digest iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc object + * @name ng.$rootScope + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide + * event processing life-cycle. See {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider(){ + var TTL = 10; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + this.$get = ['$injector', '$exceptionHandler', '$parse', + function( $injector, $exceptionHandler, $parse) { + + /** + * @ngdoc function + * @name ng.$rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link AUTO.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) + * + * Here is a simple scope snippet to show how you can interact with the scope. + *
    +        angular.injector(['ng']).invoke(function($rootScope) {
    +           var scope = $rootScope.$new();
    +           scope.salutation = 'Hello';
    +           scope.name = 'World';
    +
    +           expect(scope.greeting).toEqual(undefined);
    +
    +           scope.$watch('name', function() {
    +             scope.greeting = scope.salutation + ' ' + scope.name + '!';
    +           }); // initialize the watch
    +
    +           expect(scope.greeting).toEqual(undefined);
    +           scope.name = 'Misko';
    +           // still old value, since watches have not been called yet
    +           expect(scope.greeting).toEqual(undefined);
    +
    +           scope.$digest(); // fire all  the watches
    +           expect(scope.greeting).toEqual('Hello Misko!');
    +        });
    +     * 
    + * + * # Inheritance + * A scope can inherit from a parent scope, as in this example: + *
    +         var parent = $rootScope;
    +         var child = parent.$new();
    +
    +         parent.salutation = "Hello";
    +         child.name = "World";
    +         expect(child.salutation).toEqual('Hello');
    +
    +         child.salutation = "Welcome";
    +         expect(child.salutation).toEqual('Welcome');
    +         expect(parent.salutation).toEqual('Hello');
    +     * 
    + * + * + * @param {Object.=} providers Map of service factory which need to be provided + * for the current scope. Defaults to {@link ng}. + * @param {Object.=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy when unit-testing and having + * the need to override a default service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this['this'] = this.$root = this; + this.$$destroyed = false; + this.$$asyncQueue = []; + this.$$listeners = {}; + this.$$isolateBindings = {}; + } + + /** + * @ngdoc property + * @name ng.$rootScope.Scope#$id + * @propertyOf ng.$rootScope.Scope + * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for + * debugging. + */ + + + Scope.prototype = { + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$new + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and + * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope + * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for + * the scope and its child scopes to be permanently detached from the parent and thus stop + * participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate if true then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets it is useful for the widget to not accidentally read parent + * state. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate) { + var Child, + child; + + if (isFunction(isolate)) { + // TODO: remove at some point + throw Error('API-CHANGE: Use $controller to instantiate controllers.'); + } + if (isolate) { + child = new Scope(); + child.$root = this.$root; + } else { + Child = function() {}; // should be anonymous; This is so that when the minifier munges + // the name it does not become random set of chars. These will then show up as class + // name in the debugger. + Child.prototype = this; + child = new Child(); + child.$id = nextUid(); + } + child['this'] = child; + child.$$listeners = {}; + child.$parent = this; + child.$$asyncQueue = []; + child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null; + child.$$prevSibling = this.$$childTail; + if (this.$$childHead) { + this.$$childTail.$$nextSibling = child; + this.$$childTail = child; + } else { + this.$$childHead = this.$$childTail = child; + } + return child; + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$watch + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and + * should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} + * reruns when it detects changes the `watchExpression` can execute multiple times per + * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). The inequality is determined according to + * {@link angular.equals} function. To save the value of the object for later comparison, the + * {@link angular.copy} function is used. It also means that watching complex options will + * have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This + * is achieved by rerunning the watchers until no changes are detected. The rerun iteration + * limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` + * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is + * detected, be prepared for multiple calls to your listener.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * + * # Example + *
    +           // let's assume that scope was dependency injected as the $rootScope
    +           var scope = $rootScope;
    +           scope.name = 'misko';
    +           scope.counter = 0;
    +
    +           expect(scope.counter).toEqual(0);
    +           scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
    +           expect(scope.counter).toEqual(0);
    +
    +           scope.$digest();
    +           // no variable change
    +           expect(scope.counter).toEqual(0);
    +
    +           scope.name = 'adam';
    +           scope.$digest();
    +           expect(scope.counter).toEqual(1);
    +       * 
    + * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a + * call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {(function()|string)=} listener Callback called whenever the return value of + * the `watchExpression` changes. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. + * + * @param {boolean=} objectEquality Compare object for equality rather than for reference. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality) { + var scope = this, + get = compileToFn(watchExp, 'watch'), + array = scope.$$watchers, + watcher = { + fn: listener, + last: initWatchVal, + get: get, + exp: watchExp, + eq: !!objectEquality + }; + + // in the case user pass string, we need to compile it, do we really need this ? + if (!isFunction(listener)) { + var listenFn = compileToFn(listener || noop, 'listener'); + watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; + } + + if (!array) { + array = scope.$$watchers = []; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + + return function() { + arrayRemove(array, watcher); + }; + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$digest + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. + * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the + * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are + * firing. This means that it is possible to get into an infinite loop. This function will throw + * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. + * + * Usually you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead a call to {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a + * {@link ng.$compileProvider#directive directives}) will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with {@link ng.$rootScope.Scope#$watch $watch()} + * with no `listener`. + * + * You may have a need to call `$digest()` from within unit-tests, to simulate the scope + * life-cycle. + * + * # Example + *
    +           var scope = ...;
    +           scope.name = 'misko';
    +           scope.counter = 0;
    +
    +           expect(scope.counter).toEqual(0);
    +           scope.$watch('name', function(newValue, oldValue) {
    +             scope.counter = scope.counter + 1;
    +           });
    +           expect(scope.counter).toEqual(0);
    +
    +           scope.$digest();
    +           // no variable change
    +           expect(scope.counter).toEqual(0);
    +
    +           scope.name = 'adam';
    +           scope.$digest();
    +           expect(scope.counter).toEqual(1);
    +       * 
    + * + */ + $digest: function() { + var watch, value, last, + watchers, + asyncQueue, + length, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, logMsg; + + beginPhase('$digest'); + + do { + dirty = false; + current = target; + do { + asyncQueue = current.$$asyncQueue; + while(asyncQueue.length) { + try { + current.$eval(asyncQueue.shift()); + } catch (e) { + $exceptionHandler(e); + } + } + if ((watchers = current.$$watchers)) { + // process our watches + length = watchers.length; + while (length--) { + try { + watch = watchers[length]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if ((value = watch.get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (typeof value == 'number' && typeof last == 'number' + && isNaN(value) && isNaN(last)))) { + dirty = true; + watch.last = watch.eq ? copy(value) : value; + watch.fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + logMsg = (isFunction(watch.exp)) + ? 'fn: ' + (watch.exp.name || watch.exp.toString()) + : watch.exp; + logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); + watchLog[logIdx].push(logMsg); + } + } + } catch (e) { + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { + while(current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + if(dirty && !(ttl--)) { + clearPhase(); + throw Error(TTL + ' $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); + } + } while (dirty || asyncQueue.length); + + clearPhase(); + }, + + + /** + * @ngdoc event + * @name ng.$rootScope.Scope#$destroy + * @eventOf ng.$rootScope.Scope + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + */ + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$destroy + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it chance to + * perform any necessary cleanup. + */ + $destroy: function() { + // we can't destroy the root scope or a scope that has been already destroyed + if ($rootScope == this || this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + + if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; + if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // This is bogus code that works around Chrome's GC leak + // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = + this.$$childTail = null; + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$eval + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Executes the `expression` on the current scope returning the result. Any exceptions in the + * expression are propagated (uncaught). This is useful when evaluating Angular expressions. + * + * # Example + *
    +           var scope = ng.$rootScope.Scope();
    +           scope.a = 1;
    +           scope.b = 2;
    +
    +           expect(scope.$eval('a+b')).toEqual(3);
    +           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
    +       * 
    + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$evalAsync + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: + * + * - it will execute in the current script execution context (before any DOM rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + */ + $evalAsync: function(expr) { + this.$$asyncQueue.push(expr); + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$apply + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * `$apply()` is used to execute an expression in angular from outside of the angular framework. + * (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life-cycle + * of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * ## Life cycle + * + * # Pseudo-Code of `$apply()` + *
    +           function $apply(expr) {
    +             try {
    +               return $eval(expr);
    +             } catch (e) {
    +               $exceptionHandler(e);
    +             } finally {
    +               $root.$digest();
    +             }
    +           }
    +       * 
    + * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression + * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + return this.$eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + clearPhase(); + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + throw e; + } + } + }, + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$on + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of + * event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the current scope which is handling the event. + * - `name` - `{string}`: Name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event + * propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, args...)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + return function() { + namedListeners[indexOf(namedListeners, listener)] = null; + }; + }, + + + /** + * @ngdoc function + * @name ng.$rootScope.Scope#$emit + * @methodOf ng.$rootScope.Scope + * @function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. + * Afterwards, the event traverses upwards toward the root scope and calls all registered + * listeners along the way. The event will stop propagating if one of the listeners cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional set of arguments which will be passed onto the event listeners. + * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i=0, length=namedListeners.length; i 7), + hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + if (event == 'input' && msie == 9) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = $window.document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + // TODO(i): currently there is no way to feature detect CSP without triggering alerts + csp: false + }; + }]; +} + +/** + * @ngdoc object + * @name ng.$window + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In angular we always refer to it through the + * `$window` service, so it may be overriden, removed or mocked for testing. + * + * All expressions are evaluated with respect to current scope so they don't + * suffer from window globality. + * + * @example + + + +
    + + +
    +
    + + it('should display the greeting in the input box', function() { + input('greeting').enter('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + +
    + */ +function $WindowProvider(){ + this.$get = valueFn(window); +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = {}, key, val, i; + + if (!headers) return parsed; + + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + key = lowercase(trim(line.substr(0, i))); + val = trim(line.substr(i + 1)); + + if (key) { + if (parsed[key]) { + parsed[key] += ', ' + val; + } else { + parsed[key] = val; + } + } + }); + + return parsed; +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj = isObject(headers) ? headers : undefined; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + return headersObj[lowercase(name)] || null; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers Http headers getter fn. + * @param {(function|Array.)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, fns) { + if (isFunction(fns)) + return fns(data, headers); + + forEach(fns, function(fn) { + data = fn(data, headers); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +function $HttpProvider() { + var JSON_START = /^\s*(\[|\{[^\{])/, + JSON_END = /[\}\]]\s*$/, + PROTECTION_PREFIX = /^\)\]\}',?\n/; + + var $config = this.defaults = { + // transform incoming response data + transformResponse: [function(data) { + if (isString(data)) { + // strip json vulnerability protection prefix + data = data.replace(PROTECTION_PREFIX, ''); + if (JSON_START.test(data) && JSON_END.test(data)) + data = fromJson(data, true); + } + return data; + }], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*', + 'X-Requested-With': 'XMLHttpRequest' + }, + post: {'Content-Type': 'application/json;charset=utf-8'}, + put: {'Content-Type': 'application/json;charset=utf-8'} + } + }; + + var providerResponseInterceptors = this.responseInterceptors = []; + + this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', + function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { + + var defaultCache = $cacheFactory('$http'), + responseInterceptors = []; + + forEach(providerResponseInterceptors, function(interceptor) { + responseInterceptors.push( + isString(interceptor) + ? $injector.get(interceptor) + : $injector.invoke(interceptor) + ); + }); + + + /** + * @ngdoc function + * @name ng.$http + * @requires $httpBackend + * @requires $browser + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core Angular service that facilitates communication with the remote + * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest + * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}. + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * # General usage + * The `$http` service is a function which takes a single argument — a configuration object — + * that is used to generate an HTTP request and returns a {@link ng.$q promise} + * with two $http specific methods: `success` and `error`. + * + *
    +     *   $http({method: 'GET', url: '/someUrl'}).
    +     *     success(function(data, status, headers, config) {
    +     *       // this callback will be called asynchronously
    +     *       // when the response is available
    +     *     }).
    +     *     error(function(data, status, headers, config) {
    +     *       // called asynchronously if an error occurs
    +     *       // or server returns response with an error status.
    +     *     });
    +     * 
    + * + * Since the returned value of calling the $http function is a `promise`, you can also use + * the `then` method to register callbacks, and these callbacks will receive a single argument – + * an object representing the response. See the API signature and type info below for more + * details. + * + * A response status code between 200 and 299 is considered a success status and + * will result in the success callback being called. Note that if the response is a redirect, + * XMLHttpRequest will transparently follow it, meaning that the error callback will not be + * called for such responses. + * + * # Shortcut methods + * + * Since all invocations of the $http service require passing in an HTTP method and URL, and + * POST/PUT requests require request data to be provided as well, shortcut methods + * were created: + * + *
    +     *   $http.get('/someUrl').success(successCallback);
    +     *   $http.post('/someUrl', data).success(successCallback);
    +     * 
    + * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * + * + * # Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - `Accept: application/json, text/plain, * / *` + * - `X-Requested-With: XMLHttpRequest` + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get['My-Header']='value'`. + * + * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same + * fashion. + * + * + * # Transforming Requests and Responses + * + * Both requests and responses can be transformed using transform functions. By default, Angular + * applies these transformations: + * + * Request transformations: + * + * - If the `data` property of the request configuration object contains an object, serialize it into + * JSON format. + * + * Response transformations: + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If JSON response is detected, deserialize it using a JSON parser. + * + * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and + * `$httpProvider.defaults.transformResponse` properties. These properties are by default an + * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the + * transformation chain. You can also decide to completely override any default transformations by assigning your + * transformation functions to these properties directly without the array wrapper. + * + * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or + * `transformResponse` properties of the configuration object passed into `$http`. + * + * + * # Caching + * + * To enable caching, set the configuration property `cache` to `true`. When the cache is + * enabled, `$http` stores the response from the server in local cache. Next time the + * response is served from the cache without sending a request to the server. + * + * Note that even if the response is served from cache, delivery of the data is asynchronous in + * the same way that real requests are. + * + * If there are multiple GET requests for the same URL that should be cached using the same + * cache, but the cache is not populated yet, only one request to the server will be made and + * the remaining requests will be fulfilled using the response from the first request. + * + * + * # Response interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication or any kind of synchronous or + * asynchronous preprocessing of received responses, it is desirable to be able to intercept + * responses for http requests before they are handed over to the application code that + * initiated these requests. The response interceptors leverage the {@link ng.$q + * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. + * + * The interceptors are service factories that are registered with the $httpProvider by + * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor — a function that + * takes a {@link ng.$q promise} and returns the original or a new promise. + * + *
    +     *   // register the interceptor as a service
    +     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
    +     *     return function(promise) {
    +     *       return promise.then(function(response) {
    +     *         // do something on success
    +     *       }, function(response) {
    +     *         // do something on error
    +     *         if (canRecover(response)) {
    +     *           return responseOrNewPromise
    +     *         }
    +     *         return $q.reject(response);
    +     *       });
    +     *     }
    +     *   });
    +     *
    +     *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
    +     *
    +     *
    +     *   // register the interceptor via an anonymous factory
    +     *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
    +     *     return function(promise) {
    +     *       // same as above
    +     *     }
    +     *   });
    +     * 
    + * + * + * # Security Considerations + * + * When designing web applications, consider security threats from: + * + * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx + * JSON vulnerability} + * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} + * + * Both server and the client must cooperate in order to eliminate these threats. Angular comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ## JSON Vulnerability Protection + * + * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx + * JSON vulnerability} allows third party website to turn your JSON resource URL into + * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * Angular will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + *
    +     * ['one','two']
    +     * 
    + * + * which is vulnerable to attack, your server can return: + *
    +     * )]}',
    +     * ['one','two']
    +     * 
    + * + * Angular will strip the prefix, before processing the JSON. + * + * + * ## Cross Site Request Forgery (XSRF) Protection + * + * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which + * an unauthorized site can gain your user's private data. Angular provides a mechanism + * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie + * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that + * runs on your domain could read the cookie, your server can be assured that the XHR came from + * JavaScript running on your domain. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from making + * up its own tokens). We recommend that the token is a digest of your site's authentication + * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security. + * + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **params** – `{Object.}` – Map of strings or objects which will be turned to + * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. + * - **transformRequest** – `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * - **transformResponse** – `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body and headers and returns its transformed (typically deserialized) version. + * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the + * GET request, otherwise if a cache instance built with + * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for + * caching. + * - **timeout** – `{number}` – timeout in milliseconds. + * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the + * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 + * requests with credentials} for more information. + * + * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the + * standard `then` method and two http specific methods: `success` and `error`. The `then` + * method takes two arguments a success and an error callback which will be called with a + * response object. The `success` and `error` methods take a single argument - a function that + * will be called when the request succeeds or fails respectively. The arguments passed into + * these functions are destructured representation of the response object passed into the + * `then` method. The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * + * @property {Array.} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example + + +
    + + +
    + + + +
    http status code: {{status}}
    +
    http response data: {{data}}
    +
    +
    + + function FetchCtrl($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + success(function(data, status) { + $scope.status = status; + $scope.data = data; + }). + error(function(data, status) { + $scope.data = data || "Request failed"; + $scope.status = status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + } + + + Hello, $http! + + + it('should make an xhr GET request', function() { + element(':button:contains("Sample GET")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Hello, \$http!/); + }); + + it('should make a JSONP request to angularjs.org', function() { + element(':button:contains("Sample JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('200'); + expect(binding('data')).toMatch(/Super Hero!/); + }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + element(':button:contains("Invalid JSONP")').click(); + element(':button:contains("fetch")').click(); + expect(binding('status')).toBe('0'); + expect(binding('data')).toBe('Request failed'); + }); + +
    + */ + function $http(config) { + config.method = uppercase(config.method); + + var reqTransformFn = config.transformRequest || $config.transformRequest, + respTransformFn = config.transformResponse || $config.transformResponse, + defHeaders = $config.headers, + reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, + defHeaders.common, defHeaders[lowercase(config.method)], config.headers), + reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn), + promise; + + // strip content-type if data is undefined + if (isUndefined(config.data)) { + delete reqHeaders['Content-Type']; + } + + // send request + promise = sendReq(config, reqData, reqHeaders); + + + // transform future response + promise = promise.then(transformResponse, transformResponse); + + // apply interceptors + forEach(responseInterceptors, function(interceptor) { + promise = interceptor(promise); + }); + + promise.success = function(fn) { + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function(fn) { + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response, { + data: transformData(response.data, response.headers, respTransformFn) + }); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name ng.$http#get + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#delete + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#head + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#jsonp + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `JSONP` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request. + * Should contain `JSON_CALLBACK` string. + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name ng.$http#post + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name ng.$http#put + * @methodOf ng.$http + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put'); + + /** + * @ngdoc property + * @name ng.$http#defaults + * @propertyOf ng.$http + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = $config; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend(config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend(config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData, reqHeaders) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + url = buildUrl(config.url, config.params); + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + + if (config.cache && config.method == 'GET') { + cache = isObject(config.cache) ? config.cache : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (cachedResp) { + if (cachedResp.then) { + // cached request has already been sent, but there is no response yet + cachedResp.then(removePendingReq, removePendingReq); + return cachedResp; + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2])); + } else { + resolvePromise(cachedResp, 200, {}); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + // if we won't have the response in cache, send the request to the backend + if (!cachedResp) { + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials); + } + + return promise; + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString)]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + resolvePromise(response, status, headersString); + $rootScope.$apply(); + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers) { + // normalize internal statuses to 0 + status = Math.max(status, 0); + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config + }); + } + + + function removePendingReq() { + var idx = indexOf($http.pendingRequests, config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, params) { + if (!params) return url; + var parts = []; + forEachSorted(params, function(value, key) { + if (value == null || value == undefined) return; + if (isObject(value)) { + value = toJson(value); + } + parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); + } + + + }]; +} + +var XHR = window.XMLHttpRequest || function() { + try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} + try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} + try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} + throw new Error("This browser does not support XMLHttpRequest."); +}; + + +/** + * @ngdoc object + * @name ng.$httpBackend + * @requires $browser + * @requires $window + * @requires $document + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { + return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, + $document[0], $window.location.protocol.replace(':', '')); + }]; +} + +function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) { + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials) { + $browser.$$incOutstandingRequestCount(); + url = url || $browser.url(); + + if (lowercase(method) == 'jsonp') { + var callbackId = '_' + (callbacks.counter++).toString(36); + callbacks[callbackId] = function(data) { + callbacks[callbackId].data = data; + }; + + jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + function() { + if (callbacks[callbackId].data) { + completeRequest(callback, 200, callbacks[callbackId].data); + } else { + completeRequest(callback, -2); + } + delete callbacks[callbackId]; + }); + } else { + var xhr = new XHR(); + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (value) xhr.setRequestHeader(key, value); + }); + + var status; + + // In IE6 and 7, this might be called synchronously when xhr.send below is called and the + // response is in the cache. the promise api will ensure that to the app code the api is + // always async + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + var responseHeaders = xhr.getAllResponseHeaders(); + + // TODO(vojta): remove once Firefox 21 gets released. + // begin: workaround to overcome Firefox CORS http response headers bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=608735 + // Firefox already patched in nightly. Should land in Firefox 21. + + // CORS "simple response headers" http://www.w3.org/TR/cors/ + var value, + simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type", + "Expires", "Last-Modified", "Pragma"]; + if (!responseHeaders) { + responseHeaders = ""; + forEach(simpleHeaders, function (header) { + var value = xhr.getResponseHeader(header); + if (value) { + responseHeaders += header + ": " + value + "\n"; + } + }); + } + // end of the workaround. + + completeRequest(callback, status || xhr.status, xhr.responseText, + responseHeaders); + } + }; + + if (withCredentials) { + xhr.withCredentials = true; + } + + xhr.send(post || ''); + + if (timeout > 0) { + $browserDefer(function() { + status = -1; + xhr.abort(); + }, timeout); + } + } + + + function completeRequest(callback, status, response, headersString) { + // URL_MATCH is defined in src/service/location.js + var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1]; + + // fix status code for file protocol (it's always 0) + status = (protocol == 'file') ? (response ? 200 : 404) : status; + + // normalize IE bug (http://bugs.jquery.com/ticket/1450) + status = status == 1223 ? 204 : status; + + callback(status, response, headersString); + $browser.$$completeOutstandingRequest(noop); + } + }; + + function jsonpReq(url, done) { + // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), + doneWrapper = function() { + rawDocument.body.removeChild(script); + if (done) done(); + }; + + script.type = 'text/javascript'; + script.src = url; + + if (msie) { + script.onreadystatechange = function() { + if (/loaded|complete/.test(script.readyState)) doneWrapper(); + }; + } else { + script.onload = script.onerror = doneWrapper; + } + + rawDocument.body.appendChild(script); + } +} + +/** + * @ngdoc object + * @name ng.$locale + * + * @description + * $locale service provides localization rules for various Angular components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ +function $LocaleProvider(){ + this.$get = function() { + return { + id: 'en-us', + + NUMBER_FORMATS: { + DECIMAL_SEP: '.', + GROUP_SEP: ',', + PATTERNS: [ + { // Decimal Pattern + minInt: 1, + minFrac: 0, + maxFrac: 3, + posPre: '', + posSuf: '', + negPre: '-', + negSuf: '', + gSize: 3, + lgSize: 3 + },{ //Currency Pattern + minInt: 1, + minFrac: 2, + maxFrac: 2, + posPre: '\u00A4', + posSuf: '', + negPre: '(\u00A4', + negSuf: ')', + gSize: 3, + lgSize: 3 + } + ], + CURRENCY_SYM: '$' + }, + + DATETIME_FORMATS: { + MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' + .split(','), + SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), + AMPMS: ['AM','PM'], + medium: 'MMM d, y h:mm:ss a', + short: 'M/d/yy h:mm a', + fullDate: 'EEEE, MMMM d, y', + longDate: 'MMMM d, y', + mediumDate: 'MMM d, y', + shortDate: 'M/d/yy', + mediumTime: 'h:mm:ss a', + shortTime: 'h:mm a' + }, + + pluralCat: function(num) { + if (num === 1) { + return 'one'; + } + return 'other'; + } + }; + }; +} + +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', + function($rootScope, $browser, $q, $exceptionHandler) { + var deferreds = {}; + + + /** + * @ngdoc function + * @name ng.$timeout + * @requires $browser + * + * @description + * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of registering a timeout function is a promise, which will be resolved when + * the timeout is reached and the timeout function is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * @param {function()} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this + * promise will be resolved with is the return value of the `fn` function. + */ + function timeout(fn, delay, invokeApply) { + var deferred = $q.defer(), + promise = deferred.promise, + skipApply = (isDefined(invokeApply) && !invokeApply), + timeoutId, cleanup; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn()); + } catch(e) { + deferred.reject(e); + $exceptionHandler(e); + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + cleanup = function() { + delete deferreds[promise.$$timeoutId]; + }; + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + promise.then(cleanup, cleanup); + + return promise; + } + + + /** + * @ngdoc function + * @name ng.$timeout#cancel + * @methodOf ng.$timeout + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + deferreds[promise.$$timeoutId].reject('canceled'); + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +/** + * @ngdoc object + * @name ng.$filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To + * achieve this a filter definition consists of a factory function which is annotated with dependencies and is + * responsible for creating a filter function. + * + *
    + *   // Filter registration
    + *   function MyModule($provide, $filterProvider) {
    + *     // create a service to demonstrate injection (not always needed)
    + *     $provide.value('greet', function(name){
    + *       return 'Hello ' + name + '!';
    + *     });
    + *
    + *     // register a filter factory which uses the
    + *     // greet service to demonstrate DI.
    + *     $filterProvider.register('greet', function(greet){
    + *       // return the filter function which uses the greet service
    + *       // to generate salutation
    + *       return function(text) {
    + *         // filters need to be forgiving so check input validity
    + *         return text && greet(text) || text;
    + *       };
    + *     });
    + *   }
    + * 
    + * + * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`. + *
    + *   it('should be the same instance', inject(
    + *     function($filterProvider) {
    + *       $filterProvider.register('reverse', function(){
    + *         return ...;
    + *       });
    + *     },
    + *     function($filter, reverseFilter) {
    + *       expect($filter('reverse')).toBe(reverseFilter);
    + *     });
    + * 
    + * + * + * For more information about how angular filters work, and how to create your own filters, see + * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer + * Guide. + */ +/** + * @ngdoc method + * @name ng.$filterProvider#register + * @methodOf ng.$filterProvider + * @description + * Register filter factory function. + * + * @param {String} name Name of the filter. + * @param {function} fn The filter factory function which is injectable. + */ + + +/** + * @ngdoc function + * @name ng.$filter + * @function + * @description + * Filters are used for formatting data displayed to the user. + * + * The general syntax in templates is as follows: + * + * {{ expression [| filter_name[:parameter_value] ... ] }} + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + */ +$FilterProvider.$inject = ['$provide']; +function $FilterProvider($provide) { + var suffix = 'Filter'; + + function register(name, factory) { + return $provide.factory(name + suffix, factory); + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + } + }]; + + //////////////////////////////////////// + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name ng.filter:filter + * @function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: Predicate that results in a substring match using the value of `expression` + * string. All strings or objects with string properties in `array` that contain this string + * will be returned. The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object. That's equivalent to the simple substring match with a `string` + * as described above. + * + * - `function`: A predicate function can be used to write arbitrary filters. The function is + * called for each element of `array`. The final result is an array of those elements that + * the predicate returned true for. + * + * @example + + +
    + + Search: + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    + Any:
    + Name only
    + Phone only
    + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    + + it('should search across all fields when filtering with a string', function() { + input('searchText').enter('m'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Adam']); + + input('searchText').enter('76'); + expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). + toEqual(['John', 'Julie']); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + input('search.$').enter('i'); + expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Mike', 'Julie']); + }); + +
    + */ +function filterFilter() { + return function(array, expression) { + if (!isArray(array)) return array; + var predicates = []; + predicates.check = function(value) { + for (var j = 0; j < predicates.length; j++) { + if(!predicates[j](value)) { + return false; + } + } + return true; + }; + var search = function(obj, text){ + if (text.charAt(0) === '!') { + return !search(obj, text.substr(1)); + } + switch (typeof obj) { + case "boolean": + case "number": + case "string": + return ('' + obj).toLowerCase().indexOf(text) > -1; + case "object": + for ( var objKey in obj) { + if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { + return true; + } + } + return false; + case "array": + for ( var i = 0; i < obj.length; i++) { + if (search(obj[i], text)) { + return true; + } + } + return false; + default: + return false; + } + }; + switch (typeof expression) { + case "boolean": + case "number": + case "string": + expression = {$:expression}; + case "object": + for (var key in expression) { + if (key == '$') { + (function() { + var text = (''+expression[key]).toLowerCase(); + if (!text) return; + predicates.push(function(value) { + return search(value, text); + }); + })(); + } else { + (function() { + var path = key; + var text = (''+expression[key]).toLowerCase(); + if (!text) return; + predicates.push(function(value) { + return search(getter(value, path), text); + }); + })(); + } + } + break; + case 'function': + predicates.push(expression); + break; + default: + return array; + } + var filtered = []; + for ( var j = 0; j < array.length; j++) { + var value = array[j]; + if (predicates.check(value)) { + filtered.push(value); + } + } + return filtered; + } +} + +/** + * @ngdoc filter + * @name ng.filter:currency + * @function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @returns {string} Formatted number. + * + * + * @example + + + +
    +
    + default currency symbol ($): {{amount | currency}}
    + custom currency identifier (USD$): {{amount | currency:"USD$"}} +
    +
    + + it('should init with 1234.56', function() { + expect(binding('amount | currency')).toBe('$1,234.56'); + expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); + }); + it('should update', function() { + input('amount').enter('-1234'); + expect(binding('amount | currency')).toBe('($1,234.00)'); + expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); + }); + +
    + */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol){ + if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; + return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). + replace(/\u00A4/g, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name ng.filter:number + * @function + * + * @description + * Formats a number as text. + * + * If the input is not a number an empty string is returned. + * + * @param {number|string} number Number to format. + * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. + * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. + * + * @example + + + +
    + Enter number:
    + Default formatting: {{val | number}}
    + No fractions: {{val | number:0}}
    + Negative number: {{-val | number:4}} +
    +
    + + it('should format numbers', function() { + expect(binding('val | number')).toBe('1,234.568'); + expect(binding('val | number:0')).toBe('1,235'); + expect(binding('-val | number:4')).toBe('-1,234.5679'); + }); + + it('should update', function() { + input('val').enter('3374.333'); + expect(binding('val | number')).toBe('3,374.333'); + expect(binding('val | number:0')).toBe('3,374'); + expect(binding('-val | number:4')).toBe('-3,374.3330'); + }); + +
    + */ + + +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +var DECIMAL_SEP = '.'; +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + if (isNaN(number) || !isFinite(number)) return ''; + + var isNegative = number < 0; + number = Math.abs(number); + var numStr = number + '', + formatedText = '', + parts = []; + + var hasExponent = false; + if (numStr.indexOf('e') !== -1) { + var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); + if (match && match[2] == '-' && match[3] > fractionSize + 1) { + numStr = '0'; + } else { + formatedText = numStr; + hasExponent = true; + } + } + + if (!hasExponent) { + var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; + + // determine fractionSize if it is not specified + if (isUndefined(fractionSize)) { + fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); + } + + var pow = Math.pow(10, fractionSize); + number = Math.round(number * pow) / pow; + var fraction = ('' + number).split(DECIMAL_SEP); + var whole = fraction[0]; + fraction = fraction[1] || ''; + + var pos = 0, + lgroup = pattern.lgSize, + group = pattern.gSize; + + if (whole.length >= (lgroup + group)) { + pos = whole.length - lgroup; + for (var i = 0; i < pos; i++) { + if ((pos - i)%group === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + } + + for (i = pos; i < whole.length; i++) { + if ((whole.length - i)%lgroup === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + + // format fraction part. + while(fraction.length < fractionSize) { + fraction += '0'; + } + + if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); + } + + parts.push(isNegative ? pattern.negPre : pattern.posPre); + parts.push(formatedText); + parts.push(isNegative ? pattern.negSuf : pattern.posSuf); + return parts.join(''); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while(num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} + + +function dateGetter(name, size, offset, trim) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) + value += offset; + if (value === 0 && offset == -12 ) value = 12; + return padNumber(value, size, trim); + }; +} + +function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date['get' + name](); + var get = uppercase(shortForm ? ('SHORT' + name) : name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset(); + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4), + yy: dateGetter('FullYear', 2, 0, true), + y: dateGetter('FullYear', 1), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, + NUMBER_STRING = /^\d+$/; + +/** + * @ngdoc filter + * @name ng.filter:date + * @function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in am/pm, padded (01-12) + * * `'h'`: Hour in am/pm, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'a'`: am/pm marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 pm) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010 + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) + * + * `format` string can contain literal values. These need to be quoted with single quotes (e.g. + * `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence + * (e.g. `"h o''clock"`). + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + + + {{1288323623006 | date:'medium'}}: + {{1288323623006 | date:'medium'}}
    + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
    + {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: + {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
    +
    + + it('should format date', function() { + expect(binding("1288323623006 | date:'medium'")). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")). + toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); + expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + }); + +
    + */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + function jsonStringToDate(string){ + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0; + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); + date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); + return date; + } + return string; + } + + + return function(date, format) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + if (NUMBER_STRING.test(date)) { + date = int(date); + } else { + date = jsonStringToDate(date); + } + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date)) { + return date; + } + + while(format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + forEach(parts, function(value){ + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS) + : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name ng.filter:json + * @function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @returns {string} JSON string. + * + * + * @example: + + +
    {{ {'name':'value'} | json }}
    +
    + + it('should jsonify filtered objects', function() { + expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/); + }); + +
    + * + */ +function jsonFilter() { + return function(object) { + return toJson(object, true); + }; +} + + +/** + * @ngdoc filter + * @name ng.filter:lowercase + * @function + * @description + * Converts string to lowercase. + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name ng.filter:uppercase + * @function + * @description + * Converts string to uppercase. + * @see angular.uppercase + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc function + * @name ng.filter:limitTo + * @function + * + * @description + * Creates a new array containing only a specified number of elements in an array. The elements + * are taken from either the beginning or the end of the source array, as specified by the + * value and sign (positive or negative) of `limit`. + * + * Note: This function is used to augment the `Array` type in Angular expressions. See + * {@link ng.$filter} for more information about Angular arrays. + * + * @param {Array} array Source array to be limited. + * @param {string|Number} limit The length of the returned array. If the `limit` number is + * positive, `limit` number of items from the beginning of the source array are copied. + * If the number is negative, `limit` number of items from the end of the source array are + * copied. The `limit` will be trimmed if it exceeds `array.length` + * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit` + * elements. + * + * @example + + + +
    + Limit {{numbers}} to: +

    Output: {{ numbers | limitTo:limit }}

    +
    +
    + + it('should limit the numer array to first three items', function() { + expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3'); + expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]'); + }); + + it('should update the output when -3 is entered', function() { + input('limit').enter(-3); + expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]'); + }); + + it('should not exceed the maximum size of input array', function() { + input('limit').enter(100); + expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]'); + }); + +
    + */ +function limitToFilter(){ + return function(array, limit) { + if (!(array instanceof Array)) return array; + limit = int(limit); + var out = [], + i, n; + + // check that array is iterable + if (!array || !(array instanceof Array)) + return out; + + // if abs(limit) exceeds maximum length, trim it + if (limit > array.length) + limit = array.length; + else if (limit < -array.length) + limit = -array.length; + + if (limit > 0) { + i = 0; + n = limit; + } else { + i = array.length + limit; + n = array.length; + } + + for (; i} expression A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `=`, `>` operator. + * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' + * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control + * ascending or descending sort order (for example, +name or -name). + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * @param {boolean=} reverse Reverse the order the array. + * @returns {Array} Sorted copy of the source array. + * + * @example + + + +
    +
    Sorting predicate = {{predicate}}; reverse = {{reverse}}
    +
    + [ unsorted ] + + + + + + + + + + + +
    Name + (^)Phone NumberAge
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + it('should be reverse ordered by aged', function() { + expect(binding('predicate')).toBe('-age'); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '29', '21', '19', '10']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); + }); + + it('should reorder the table when user selects different predicate', function() { + element('.doc-example-live a:contains("Name")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); + expect(repeater('table.friend', 'friend in friends').column('friend.age')). + toEqual(['35', '10', '29', '19', '21']); + + element('.doc-example-live a:contains("Phone")').click(); + expect(repeater('table.friend', 'friend in friends').column('friend.phone')). + toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); + expect(repeater('table.friend', 'friend in friends').column('friend.name')). + toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); + }); + +
    + */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse){ + return function(array, sortPredicate, reverseOrder) { + if (!isArray(array)) return array; + if (!sortPredicate) return array; + sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; + sortPredicate = map(sortPredicate, function(predicate){ + var descending = false, get = predicate || identity; + if (isString(predicate)) { + if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { + descending = predicate.charAt(0) == '-'; + predicate = predicate.substring(1); + } + get = $parse(predicate); + } + return reverseComparator(function(a,b){ + return compare(get(a),get(b)); + }, descending); + }); + var arrayCopy = []; + for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } + return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); + + function comparator(o1, o2){ + for ( var i = 0; i < sortPredicate.length; i++) { + var comp = sortPredicate[i](o1, o2); + if (comp !== 0) return comp; + } + return 0; + } + function reverseComparator(comp, descending) { + return toBoolean(descending) + ? function(a,b){return comp(b,a);} + : comp; + } + function compare(v1, v2){ + var t1 = typeof v1; + var t2 = typeof v2; + if (t1 == t2) { + if (t1 == "string") v1 = v1.toLowerCase(); + if (t1 == "string") v2 = v2.toLowerCase(); + if (v1 === v2) return 0; + return v1 < v2 ? -1 : 1; + } else { + return t1 < t2 ? -1 : 1; + } + } + } +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + } + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name ng.directive:a + * @restrict E + * + * @description + * Modifies the default behavior of html A tag, so that the default action is prevented when href + * attribute is empty. + * + * The reasoning for this change is to allow easy creation of action links with `ngClick` directive + * without changing the location or causing page reloads, e.g.: + * `Save` + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + + if (msie <= 8) { + + // turn link into a stylable link in IE + // but only if it doesn't have name attribute, in which case it's an anchor + if (!attr.href && !attr.name) { + attr.$set('href', ''); + } + + // add a comment node to anchors to workaround IE bug that causes element content to be reset + // to new attribute content if attribute is updated with value containing @ and element also + // contains value with @ + // see issue #1949 + element.append(document.createComment('IE fix')); + } + + return function(scope, element) { + element.bind('click', function(event){ + // if we have no href url, then don't navigate anywhere. + if (!element.attr('href')) { + event.preventDefault(); + } + }); + } + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngHref + * @restrict A + * + * @description + * Using Angular markup like {{hash}} in an href attribute makes + * the page open to a wrong URL, if the user clicks that link before + * angular has a chance to replace the {{hash}} with actual URL, the + * link will be broken and will most likely return a 404 error. + * The `ngHref` directive solves this problem. + * + * The buggy way to write it: + *
    + * 
    + * 
    + * + * The correct way to write it: + *
    + * 
    + * 
    + * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example uses `link` variable inside `href` attribute: + + +
    +
    link 1 (link, don't reload)
    + link 2 (link, don't reload)
    + link 3 (link, reload!)
    + anchor (link, don't reload)
    + anchor (no link)
    + link (link, change location) + + + it('should execute ng-click but not reload when href without value', function() { + element('#link-1').click(); + expect(input('value').val()).toEqual('1'); + expect(element('#link-1').attr('href')).toBe(""); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element('#link-2').click(); + expect(input('value').val()).toEqual('2'); + expect(element('#link-2').attr('href')).toBe(""); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element('#link-3').attr('href')).toBe("/123"); + + element('#link-3').click(); + expect(browser().window().path()).toEqual('/123'); + }); + + it('should execute ng-click but not reload when href empty string and name specified', function() { + element('#link-4').click(); + expect(input('value').val()).toEqual('4'); + expect(element('#link-4').attr('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element('#link-5').click(); + expect(input('value').val()).toEqual('5'); + expect(element('#link-5').attr('href')).toBe(undefined); + }); + + it('should only change url when only ng-href', function() { + input('value').enter('6'); + expect(element('#link-6').attr('href')).toBe('6'); + + element('#link-6').click(); + expect(browser().location().url()).toEqual('/6'); + }); + + + */ + +/** + * @ngdoc directive + * @name ng.directive:ngSrc + * @restrict A + * + * @description + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + *
    + * 
    + * 
    + * + * The correct way to write it: + *
    + * 
    + * 
    + * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ng.directive:ngDisabled + * @restrict A + * + * @description + * + * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: + *
    + * 
    + * + *
    + *
    + * + * The HTML specs do not require browsers to preserve the special attributes such as disabled. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngDisabled` directive. + * + * @example + + + Click me to toggle:
    + +
    + + it('should toggle button', function() { + expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngDisabled Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngChecked + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as checked. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngChecked` directive. + * @example + + + Check me to check both:
    + +
    + + it('should check both checkBoxes', function() { + expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy(); + input('master').check(); + expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngChecked Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMultiple + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as multiple. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngMultiple` directive. + * + * @example + + + Check me check multiple:
    + +
    + + it('should toggle multiple', function() { + expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy(); + }); + +
    + * + * @element SELECT + * @param {expression} ngMultiple Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngReadonly + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as readonly. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduce the `ngReadonly` directive. + * @example + + + Check me to make text readonly:
    + +
    + + it('should toggle readonly attr', function() { + expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy(); + input('checked').check(); + expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {string} expression Angular expression that will be evaluated. + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngSelected + * @restrict A + * + * @description + * The HTML specs do not require browsers to preserve the special attributes such as selected. + * (The presence of them means true and absence means false) + * This prevents the angular compiler from correctly retrieving the binding expression. + * To solve this problem, we introduced the `ngSelected` directive. + * @example + + + Check me to select:
    + +
    + + it('should select Greetings!', function() { + expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy(); + input('selected').check(); + expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy(); + }); + +
    + * + * @element OPTION + * @param {string} expression Angular expression that will be evaluated. + */ + + +var ngAttributeAliasDirectives = {}; + + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 100, + compile: function() { + return function(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + }; + } + }; + }; +}); + + +// ng-src, ng-href are interpolated +forEach(['src', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + attr.$observe(normalized, function(value) { + if (!value) + return; + + attr.$set(attrName, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // we use attr[attrName] value since $set can sanitize the url. + if (msie) element.prop(attrName, attr[attrName]); + }); + } + }; + }; +}); + +var nullFormCtrl = { + $addControl: noop, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop +}; + +/** + * @ngdoc object + * @name ng.directive:form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * + * @property {Object} $error Is an object hash, containing references to all invalid controls or + * forms, where: + * + * - keys are validation tokens (error names) — such as `required`, `url` or `email`), + * - values are arrays of controls or forms that are invalid with given error. + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope']; +function FormController(element, attrs) { + var form = this, + parentForm = element.parent().controller('form') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + errors = form.$error = {}; + + // init state + form.$name = attrs.name; + form.$dirty = false; + form.$pristine = true; + form.$valid = true; + form.$invalid = false; + + parentForm.$addControl(form); + + // Setup initial state of the control + element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + element. + removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). + addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); + } + + form.$addControl = function(control) { + if (control.$name && !form.hasOwnProperty(control.$name)) { + form[control.$name] = control; + } + }; + + form.$removeControl = function(control) { + if (control.$name && form[control.$name] === control) { + delete form[control.$name]; + } + forEach(errors, function(queue, validationToken) { + form.$setValidity(validationToken, true, control); + }); + }; + + form.$setValidity = function(validationToken, isValid, control) { + var queue = errors[validationToken]; + + if (isValid) { + if (queue) { + arrayRemove(queue, control); + if (!queue.length) { + invalidCount--; + if (!invalidCount) { + toggleValidCss(isValid); + form.$valid = true; + form.$invalid = false; + } + errors[validationToken] = false; + toggleValidCss(true, validationToken); + parentForm.$setValidity(validationToken, true, form); + } + } + + } else { + if (!invalidCount) { + toggleValidCss(isValid); + } + if (queue) { + if (includes(queue, control)) return; + } else { + errors[validationToken] = queue = []; + invalidCount++; + toggleValidCss(false, validationToken); + parentForm.$setValidity(validationToken, false, form); + } + queue.push(control); + + form.$valid = false; + form.$invalid = true; + } + }; + + form.$setDirty = function() { + element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); + form.$dirty = true; + form.$pristine = false; + parentForm.$setDirty(); + }; + +} + + +/** + * @ngdoc directive + * @name ng.directive:ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name ng.directive:form + * @restrict E + * + * @description + * Directive that instantiates + * {@link ng.directive:form.FormController FormController}. + * + * If `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * # Alias: {@link ng.directive:ngForm `ngForm`} + * + * In angular forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However browsers do not allow nesting of `
    ` elements, for this + * reason angular provides {@link ng.directive:ngForm `ngForm`} alias + * which behaves identical to `` but allows form nesting. + * + * + * # CSS classes + * - `ng-valid` Is set if the form is valid. + * - `ng-invalid` Is set if the form is invalid. + * - `ng-pristine` Is set if the form is pristine. + * - `ng-dirty` Is set if the form is dirty. + * + * + * # Submitting a form and preventing default action + * + * Since the role of forms in client-side Angular applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in application specific way. + * + * For this reason, Angular prevents the default action (form submission to the server) unless the + * `` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This + * is because of the following form submission rules coming from the html spec: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + * @example + + + + + userType: + Required!
    + userType = {{userType}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + +
    + + it('should initialize to model', function() { + expect(binding('userType')).toEqual('guest'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('userType').enter(''); + expect(binding('userType')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
    + */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', function($timeout) { + var formDirective = { + name: 'form', + restrict: 'E', + controller: FormController, + compile: function() { + return { + pre: function(scope, formElement, attr, controller) { + if (!attr.action) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var preventDefaultListener = function(event) { + event.preventDefault + ? event.preventDefault() + : event.returnValue = false; // IE + }; + + addEventListenerFn(formElement[0], 'submit', preventDefaultListener); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.bind('$destroy', function() { + $timeout(function() { + removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); + }, 0, false); + }); + } + + var parentFormCtrl = formElement.parent().controller('form'), + alias = attr.name || attr.ngForm; + + if (alias) { + scope[alias] = controller; + } + if (parentFormCtrl) { + formElement.bind('$destroy', function() { + parentFormCtrl.$removeControl(controller); + if (alias) { + scope[alias] = undefined; + } + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + } + }; + } + }; + + return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective; + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + +var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; +var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; +var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; + +var inputType = { + + /** + * @ngdoc inputType + * @name ng.directive:input.text + * + * @description + * Standard HTML text input with angular data binding. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Single word: + + Required! + + Single word only! + + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('text')).toEqual('guest'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if multi word', function() { + input('text').enter('hello world'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
    + */ + 'text': textInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.number + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Number: + + Required! + + Not valid number! + value = {{value}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('value')).toEqual('12'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('value').enter(''); + expect(binding('value')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if over max', function() { + input('value').enter('123'); + expect(binding('value')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
    + */ + 'number': numberInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.url + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + URL: + + Required! + + Not valid url! + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.url = {{!!myForm.$error.url}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('text')).toEqual('http://google.com/'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if not url', function() { + input('text').enter('xxx'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
    + */ + 'url': urlInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.email + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * + * @example + + + +
    + Email: + + Required! + + Not valid email! + text = {{text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.email = {{!!myForm.$error.email}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('text')).toEqual('me@example.com'); + expect(binding('myForm.input.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('text').enter(''); + expect(binding('text')).toEqual(''); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + + it('should be invalid if not email', function() { + input('text').enter('xxx'); + expect(binding('myForm.input.$valid')).toEqual('false'); + }); + +
    + */ + 'email': emailInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.radio + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string} value The value to which the expression should be set when selected. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Red
    + Green
    + Blue
    + color = {{color}}
    +
    +
    + + it('should change state', function() { + expect(binding('color')).toEqual('blue'); + + input('color').select('red'); + expect(binding('color')).toEqual('red'); + }); + +
    + */ + 'radio': radioInputType, + + + /** + * @ngdoc inputType + * @name ng.directive:input.checkbox + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngTrueValue The value to which the expression should be set when selected. + * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + Value1:
    + Value2:
    + value1 = {{value1}}
    + value2 = {{value2}}
    +
    +
    + + it('should change state', function() { + expect(binding('value1')).toEqual('true'); + expect(binding('value2')).toEqual('YES'); + + input('value1').check(); + input('value2').check(); + expect(binding('value1')).toEqual('false'); + expect(binding('value2')).toEqual('NO'); + }); + +
    + */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop +}; + + +function isEmpty(value) { + return isUndefined(value) || value === '' || value === null || value !== value; +} + + +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + + var listener = function() { + var value = trim(element.val()); + + if (ctrl.$viewValue !== value) { + scope.$apply(function() { + ctrl.$setViewValue(value); + }); + } + }; + + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut + if ($sniffer.hasEvent('input')) { + element.bind('input', listener); + } else { + var timeout; + + var deferListener = function() { + if (!timeout) { + timeout = $browser.defer(function() { + listener(); + timeout = null; + }); + } + }; + + element.bind('keydown', function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + deferListener(); + }); + + // if user paste into input using mouse, we need "change" event to catch it + element.bind('change', listener); + + // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it + if ($sniffer.hasEvent('paste')) { + element.bind('paste cut', deferListener); + } + } + + + ctrl.$render = function() { + element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); + }; + + // pattern validator + var pattern = attr.ngPattern, + patternValidator; + + var validate = function(regexp, value) { + if (isEmpty(value) || regexp.test(value)) { + ctrl.$setValidity('pattern', true); + return value; + } else { + ctrl.$setValidity('pattern', false); + return undefined; + } + }; + + if (pattern) { + if (pattern.match(/^\/(.*)\/$/)) { + pattern = new RegExp(pattern.substr(1, pattern.length - 2)); + patternValidator = function(value) { + return validate(pattern, value) + }; + } else { + patternValidator = function(value) { + var patternObj = scope.$eval(pattern); + + if (!patternObj || !patternObj.test) { + throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); + } + return validate(patternObj, value); + }; + } + + ctrl.$formatters.push(patternValidator); + ctrl.$parsers.push(patternValidator); + } + + // min length validator + if (attr.ngMinlength) { + var minlength = int(attr.ngMinlength); + var minLengthValidator = function(value) { + if (!isEmpty(value) && value.length < minlength) { + ctrl.$setValidity('minlength', false); + return undefined; + } else { + ctrl.$setValidity('minlength', true); + return value; + } + }; + + ctrl.$parsers.push(minLengthValidator); + ctrl.$formatters.push(minLengthValidator); + } + + // max length validator + if (attr.ngMaxlength) { + var maxlength = int(attr.ngMaxlength); + var maxLengthValidator = function(value) { + if (!isEmpty(value) && value.length > maxlength) { + ctrl.$setValidity('maxlength', false); + return undefined; + } else { + ctrl.$setValidity('maxlength', true); + return value; + } + }; + + ctrl.$parsers.push(maxLengthValidator); + ctrl.$formatters.push(maxLengthValidator); + } +} + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + ctrl.$parsers.push(function(value) { + var empty = isEmpty(value); + if (empty || NUMBER_REGEXP.test(value)) { + ctrl.$setValidity('number', true); + return value === '' ? null : (empty ? value : parseFloat(value)); + } else { + ctrl.$setValidity('number', false); + return undefined; + } + }); + + ctrl.$formatters.push(function(value) { + return isEmpty(value) ? '' : '' + value; + }); + + if (attr.min) { + var min = parseFloat(attr.min); + var minValidator = function(value) { + if (!isEmpty(value) && value < min) { + ctrl.$setValidity('min', false); + return undefined; + } else { + ctrl.$setValidity('min', true); + return value; + } + }; + + ctrl.$parsers.push(minValidator); + ctrl.$formatters.push(minValidator); + } + + if (attr.max) { + var max = parseFloat(attr.max); + var maxValidator = function(value) { + if (!isEmpty(value) && value > max) { + ctrl.$setValidity('max', false); + return undefined; + } else { + ctrl.$setValidity('max', true); + return value; + } + }; + + ctrl.$parsers.push(maxValidator); + ctrl.$formatters.push(maxValidator); + } + + ctrl.$formatters.push(function(value) { + + if (isEmpty(value) || isNumber(value)) { + ctrl.$setValidity('number', true); + return value; + } else { + ctrl.$setValidity('number', false); + return undefined; + } + }); +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var urlValidator = function(value) { + if (isEmpty(value) || URL_REGEXP.test(value)) { + ctrl.$setValidity('url', true); + return value; + } else { + ctrl.$setValidity('url', false); + return undefined; + } + }; + + ctrl.$formatters.push(urlValidator); + ctrl.$parsers.push(urlValidator); +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var emailValidator = function(value) { + if (isEmpty(value) || EMAIL_REGEXP.test(value)) { + ctrl.$setValidity('email', true); + return value; + } else { + ctrl.$setValidity('email', false); + return undefined; + } + }; + + ctrl.$formatters.push(emailValidator); + ctrl.$parsers.push(emailValidator); +} + +function radioInputType(scope, element, attr, ctrl) { + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + element.bind('click', function() { + if (element[0].checked) { + scope.$apply(function() { + ctrl.$setViewValue(attr.value); + }); + } + }); + + ctrl.$render = function() { + var value = attr.value; + element[0].checked = (value == ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function checkboxInputType(scope, element, attr, ctrl) { + var trueValue = attr.ngTrueValue, + falseValue = attr.ngFalseValue; + + if (!isString(trueValue)) trueValue = true; + if (!isString(falseValue)) falseValue = false; + + element.bind('click', function() { + scope.$apply(function() { + ctrl.$setViewValue(element[0].checked); + }); + }); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + ctrl.$formatters.push(function(value) { + return value === trueValue; + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name ng.directive:textarea + * @restrict E + * + * @description + * HTML textarea element control with angular data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + */ + + +/** + * @ngdoc directive + * @name ng.directive:input + * @restrict E + * + * @description + * HTML input element control with angular data-binding. Input control follows HTML5 input types + * and polyfills the HTML5 validation behavior for older browsers. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    +
    + User name: + + Required!
    + Last name: + + Too short! + + Too long!
    +
    +
    + user = {{user}}
    + myForm.userName.$valid = {{myForm.userName.$valid}}
    + myForm.userName.$error = {{myForm.userName.$error}}
    + myForm.lastName.$valid = {{myForm.lastName.$valid}}
    + myForm.lastName.$error = {{myForm.lastName.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.minlength = {{!!myForm.$error.minlength}}
    + myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}'); + expect(binding('myForm.userName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); + }); + + it('should be invalid if empty when required', function() { + input('user.name').enter(''); + expect(binding('user')).toEqual('{"last":"visitor"}'); + expect(binding('myForm.userName.$valid')).toEqual('false'); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be valid if empty when min length is set', function() { + input('user.last').enter(''); + expect(binding('user')).toEqual('{"name":"guest","last":""}'); + expect(binding('myForm.lastName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); + }); + + it('should be invalid if less than required min length', function() { + input('user.last').enter('xx'); + expect(binding('user')).toEqual('{"name":"guest"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/minlength/); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be invalid if longer than max length', function() { + input('user.last').enter('some ridiculously long name'); + expect(binding('user')) + .toEqual('{"name":"guest"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/maxlength/); + expect(binding('myForm.$valid')).toEqual('false'); + }); + +
    + */ +var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { + return { + restrict: 'E', + require: '?ngModel', + link: function(scope, element, attr, ctrl) { + if (ctrl) { + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, + $browser); + } + } + }; +}]; + +var VALID_CLASS = 'ng-valid', + INVALID_CLASS = 'ng-invalid', + PRISTINE_CLASS = 'ng-pristine', + DIRTY_CLASS = 'ng-dirty'; + +/** + * @ngdoc object + * @name ng.directive:ngModel.NgModelController + * + * @property {string} $viewValue Actual string value in the view. + * @property {*} $modelValue The value in the model, that the control is bound to. + * @property {Array.} $parsers Whenever the control reads value from the DOM, it executes + * all of these functions to sanitize / convert the value as well as validate. + * + * @property {Array.} $formatters Whenever the model value changes, it executes all of + * these functions to convert the value as well as validate. + * + * @property {Object} $error An bject hash with all errors as keys. + * + * @property {boolean} $pristine True if user has not interacted with the control yet. + * @property {boolean} $dirty True if user has already interacted with the control. + * @property {boolean} $valid True if there is no error. + * @property {boolean} $invalid True if at least one error on the control. + * + * @description + * + * `NgModelController` provides API for the `ng-model` directive. The controller contains + * services for data-binding, validation, CSS update, value formatting and parsing. It + * specifically does not contain any logic which deals with DOM rendering or listening to + * DOM events. The `NgModelController` is meant to be extended by other directives where, the + * directive provides DOM manipulation and the `NgModelController` provides the data-binding. + * + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', []). + directive('contenteditable', function() { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if(!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html(ngModel.$viewValue || ''); + }; + + // Listen for change events to enable binding + element.bind('blur keyup change', function() { + scope.$apply(read); + }); + read(); // initialize + + // Write data to the model + function read() { + ngModel.$setViewValue(element.html()); + } + } + }; + }); + + +
    +
    Change me!
    + Required! +
    + +
    +
    + + it('should data-bind and become invalid', function() { + var contentEditable = element('[contenteditable]'); + + expect(contentEditable.text()).toEqual('Change me!'); + input('userContent').enter(''); + expect(contentEditable.text()).toEqual(''); + expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); + }); + + *
    + * + */ +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', + function($scope, $exceptionHandler, $attr, $element, $parse) { + this.$viewValue = Number.NaN; + this.$modelValue = Number.NaN; + this.$parsers = []; + this.$formatters = []; + this.$viewChangeListeners = []; + this.$pristine = true; + this.$dirty = false; + this.$valid = true; + this.$invalid = false; + this.$name = $attr.name; + + var ngModelGet = $parse($attr.ngModel), + ngModelSet = ngModelGet.assign; + + if (!ngModelSet) { + throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + + ' (' + startingTag($element) + ')'); + } + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$render + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + */ + this.$render = noop; + + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + $error = this.$error = {}; // keep invalid keys here + + + // Setup initial state of the control + $element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + $element. + removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). + addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); + } + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$setValidity + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Change the validity state, and notifies the form when the control changes validity. (i.e. it + * does not notify form if given validator is already marked as invalid). + * + * This method should be called by validators - i.e. the parser or formatter functions. + * + * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign + * to `$error[validationErrorKey]=isValid` so that it is available for data-binding. + * The `validationErrorKey` should be in camelCase and will get converted into dash-case + * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` + * class and can be bound to as `{{someForm.someControl.$error.myError}}` . + * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). + */ + this.$setValidity = function(validationErrorKey, isValid) { + if ($error[validationErrorKey] === !isValid) return; + + if (isValid) { + if ($error[validationErrorKey]) invalidCount--; + if (!invalidCount) { + toggleValidCss(true); + this.$valid = true; + this.$invalid = false; + } + } else { + toggleValidCss(false); + this.$invalid = true; + this.$valid = false; + invalidCount++; + } + + $error[validationErrorKey] = !isValid; + toggleValidCss(isValid, validationErrorKey); + + parentForm.$setValidity(validationErrorKey, isValid, this); + }; + + + /** + * @ngdoc function + * @name ng.directive:ngModel.NgModelController#$setViewValue + * @methodOf ng.directive:ngModel.NgModelController + * + * @description + * Read a value from view. + * + * This method should be called from within a DOM event handler. + * For example {@link ng.directive:input input} or + * {@link ng.directive:select select} directives call it. + * + * It internally calls all `parsers` and if resulted value is valid, updates the model and + * calls all registered change listeners. + * + * @param {string} value Value from the view. + */ + this.$setViewValue = function(value) { + this.$viewValue = value; + + // change to dirty + if (this.$pristine) { + this.$dirty = true; + this.$pristine = false; + $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); + parentForm.$setDirty(); + } + + forEach(this.$parsers, function(fn) { + value = fn(value); + }); + + if (this.$modelValue !== value) { + this.$modelValue = value; + ngModelSet($scope, value); + forEach(this.$viewChangeListeners, function(listener) { + try { + listener(); + } catch(e) { + $exceptionHandler(e); + } + }) + } + }; + + // model -> value + var ctrl = this; + + $scope.$watch(function ngModelWatch() { + var value = ngModelGet($scope); + + // if scope model value and ngModel value are out of sync + if (ctrl.$modelValue !== value) { + + var formatters = ctrl.$formatters, + idx = formatters.length; + + ctrl.$modelValue = value; + while(idx--) { + value = formatters[idx](value); + } + + if (ctrl.$viewValue !== value) { + ctrl.$viewValue = value; + ctrl.$render(); + } + } + }); +}]; + + +/** + * @ngdoc directive + * @name ng.directive:ngModel + * + * @element input + * + * @description + * Is directive that tells Angular to do two-way data binding. It works together with `input`, + * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. + * + * `ngModel` is responsible for: + * + * - binding the view into the model, which other directives such as `input`, `textarea` or `select` + * require, + * - providing validation behavior (i.e. required, number, email, url), + * - keeping state of the control (valid/invalid, dirty/pristine, validation errors), + * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), + * - register the control with parent {@link ng.directive:form form}. + * + * For basic examples, how to use `ngModel`, see: + * + * - {@link ng.directive:input input} + * - {@link ng.directive:input.text text} + * - {@link ng.directive:input.checkbox checkbox} + * - {@link ng.directive:input.radio radio} + * - {@link ng.directive:input.number number} + * - {@link ng.directive:input.email email} + * - {@link ng.directive:input.url url} + * - {@link ng.directive:select select} + * - {@link ng.directive:textarea textarea} + * + */ +var ngModelDirective = function() { + return { + require: ['ngModel', '^?form'], + controller: NgModelController, + link: function(scope, element, attr, ctrls) { + // notify others, especially parent forms + + var modelCtrl = ctrls[0], + formCtrl = ctrls[1] || nullFormCtrl; + + formCtrl.$addControl(modelCtrl); + + element.bind('$destroy', function() { + formCtrl.$removeControl(modelCtrl); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ng.directive:ngChange + * @restrict E + * + * @description + * Evaluate given expression when user changes the input. + * The expression is not evaluated when the value change is coming from the model. + * + * Note, this directive requires `ngModel` to be present. + * + * @element input + * + * @example + * + * + * + *
    + * + * + *
    + * debug = {{confirmed}}
    + * counter = {{counter}} + *
    + *
    + * + * it('should evaluate the expression if changing from view', function() { + * expect(binding('counter')).toEqual('0'); + * element('#ng-change-example1').click(); + * expect(binding('counter')).toEqual('1'); + * expect(binding('confirmed')).toEqual('true'); + * }); + * + * it('should not evaluate the expression if changing from model', function() { + * element('#ng-change-example2').click(); + * expect(binding('counter')).toEqual('0'); + * expect(binding('confirmed')).toEqual('true'); + * }); + * + *
    + */ +var ngChangeDirective = valueFn({ + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + ctrl.$viewChangeListeners.push(function() { + scope.$eval(attr.ngChange); + }); + } +}); + + +var requiredDirective = function() { + return { + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element + + var validator = function(value) { + if (attr.required && (isEmpty(value) || value === false)) { + ctrl.$setValidity('required', false); + return; + } else { + ctrl.$setValidity('required', true); + return value; + } + }; + + ctrl.$formatters.push(validator); + ctrl.$parsers.unshift(validator); + + attr.$observe('required', function() { + validator(ctrl.$viewValue); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ng.directive:ngList + * + * @description + * Text input that converts between comma-separated string into an array of strings. + * + * @element input + * @param {string=} ngList optional delimiter that should be used to split the value. If + * specified in form `/something/` then the value will be converted into a regular expression. + * + * @example + + + +
    + List: + + Required! + names = {{names}}
    + myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
    + myForm.namesInput.$error = {{myForm.namesInput.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + it('should initialize to model', function() { + expect(binding('names')).toEqual('["igor","misko","vojta"]'); + expect(binding('myForm.namesInput.$valid')).toEqual('true'); + }); + + it('should be invalid if empty', function() { + input('names').enter(''); + expect(binding('names')).toEqual('[]'); + expect(binding('myForm.namesInput.$valid')).toEqual('false'); + }); + +
    + */ +var ngListDirective = function() { + return { + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + var match = /\/(.*)\//.exec(attr.ngList), + separator = match && new RegExp(match[1]) || attr.ngList || ','; + + var parse = function(viewValue) { + var list = []; + + if (viewValue) { + forEach(viewValue.split(separator), function(value) { + if (value) list.push(trim(value)); + }); + } + + return list; + }; + + ctrl.$parsers.push(parse); + ctrl.$formatters.push(function(value) { + if (isArray(value)) { + return value.join(', '); + } + + return undefined; + }); + } + }; +}; + + +var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; + +var ngValueDirective = function() { + return { + priority: 100, + compile: function(tpl, tplAttr) { + if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { + return function(scope, elm, attr) { + attr.$set('value', scope.$eval(attr.ngValue)); + }; + } else { + return function(scope, elm, attr) { + scope.$watch(attr.ngValue, function valueWatchAction(value) { + attr.$set('value', value, false); + }); + }; + } + } + }; +}; + +/** + * @ngdoc directive + * @name ng.directive:ngBind + * + * @description + * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element + * with the value of a given expression, and to update the text content when the value of that + * expression changes. + * + * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like + * `{{ expression }}` which is similar but less verbose. + * + * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when + * it's desirable to put bindings into template that is momentarily displayed by the browser in its + * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the + * bindings invisible to the user while the page is loading. + * + * An alternative solution to this problem would be using the + * {@link ng.directive:ngCloak ngCloak} directive. + * + * + * @element ANY + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. + * + * @example + * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. + + + +
    + Enter name:
    + Hello ! +
    +
    + + it('should check ng-bind', function() { + expect(using('.doc-example-live').binding('name')).toBe('Whirled'); + using('.doc-example-live').input('name').enter('world'); + expect(using('.doc-example-live').binding('name')).toBe('world'); + }); + +
    + */ +var ngBindDirective = ngDirective(function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.ngBind); + scope.$watch(attr.ngBind, function ngBindWatchAction(value) { + element.text(value == undefined ? '' : value); + }); +}); + + +/** + * @ngdoc directive + * @name ng.directive:ngBindTemplate + * + * @description + * The `ngBindTemplate` directive specifies that the element + * text should be replaced with the template in ngBindTemplate. + * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` + * expressions. (This is required since some HTML elements + * can not have SPAN elements such as TITLE, or OPTION to name a few.) + * + * @element ANY + * @param {string} ngBindTemplate template of form + * {{ expression }} to eval. + * + * @example + * Try it here: enter text in text box and watch the greeting change. + + + +
    + Salutation:
    + Name:
    +
    
    +       
    +
    + + it('should check ng-bind', function() { + expect(using('.doc-example-live').binding('salutation')). + toBe('Hello'); + expect(using('.doc-example-live').binding('name')). + toBe('World'); + using('.doc-example-live').input('salutation').enter('Greetings'); + using('.doc-example-live').input('name').enter('user'); + expect(using('.doc-example-live').binding('salutation')). + toBe('Greetings'); + expect(using('.doc-example-live').binding('name')). + toBe('user'); + }); + +
    + */ +var ngBindTemplateDirective = ['$interpolate', function($interpolate) { + return function(scope, element, attr) { + // TODO: move this to scenario runner + var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); + element.addClass('ng-binding').data('$binding', interpolateFn); + attr.$observe('ngBindTemplate', function(value) { + element.text(value); + }); + } +}]; + + +/** + * @ngdoc directive + * @name ng.directive:ngBindHtmlUnsafe + * + * @description + * Creates a binding that will innerHTML the result of evaluating the `expression` into the current + * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if + * {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too + * restrictive and when you absolutely trust the source of the content you are binding to. + * + * See {@link ngSanitize.$sanitize $sanitize} docs for examples. + * + * @element ANY + * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. + */ +var ngBindHtmlUnsafeDirective = [function() { + return function(scope, element, attr) { + element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); + scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { + element.html(value || ''); + }); + }; +}]; + +function classDirective(name, selector) { + name = 'ngClass' + name; + return ngDirective(function(scope, element, attr) { + var oldVal = undefined; + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + var ngClass = scope.$eval(attr[name]); + ngClassWatchAction(ngClass, ngClass); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + var mod = $index & 1; + if (mod !== old$index & 1) { + if (mod === selector) { + addClass(scope.$eval(attr[name])); + } else { + removeClass(scope.$eval(attr[name])); + } + } + }); + } + + + function ngClassWatchAction(newVal) { + if (selector === true || scope.$index % 2 === selector) { + if (oldVal && !equals(newVal,oldVal)) { + removeClass(oldVal); + } + addClass(newVal); + } + oldVal = copy(newVal); + } + + + function removeClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + + + function addClass(classVal) { + if (isObject(classVal) && !isArray(classVal)) { + classVal = map(classVal, function(v, k) { if (v) return k }); + } + if (classVal) { + element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); + } + } + }); +} + +/** + * @ngdoc directive + * @name ng.directive:ngClass + * + * @description + * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an + * expression that represents all classes to be added. + * + * The directive won't add duplicate classes if a particular class was already set. + * + * When the expression changes, the previously added classes are removed and only then the + * new classes are added. + * + * @element ANY + * @param {expression} ngClass {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class + * names, an array, or a map of class names to boolean values. + * + * @example + + + + +
    + Sample Text +
    + + .my-class { + color: red; + } + + + it('should check ng-class', function() { + expect(element('.doc-example-live span').prop('className')).not(). + toMatch(/my-class/); + + using('.doc-example-live').element(':button:first').click(); + + expect(element('.doc-example-live span').prop('className')). + toMatch(/my-class/); + + using('.doc-example-live').element(':button:last').click(); + + expect(element('.doc-example-live span').prop('className')).not(). + toMatch(/my-class/); + }); + +
    + */ +var ngClassDirective = classDirective('', true); + +/** + * @ngdoc directive + * @name ng.directive:ngClassOdd + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. + * + * This directive can be applied only within a scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
      +
    1. + + {{name}} + +
    2. +
    +
    + + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element('.doc-example-live li:first span').prop('className')). + toMatch(/odd/); + expect(element('.doc-example-live li:last span').prop('className')). + toMatch(/even/); + }); + +
    + */ +var ngClassOddDirective = classDirective('Odd', 0); + +/** + * @ngdoc directive + * @name ng.directive:ngClassEven + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except it works in + * conjunction with `ngRepeat` and takes affect only on odd (even) rows. + * + * This directive can be applied only within a scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The + * result of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
      +
    1. + + {{name}}       + +
    2. +
    +
    + + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element('.doc-example-live li:first span').prop('className')). + toMatch(/odd/); + expect(element('.doc-example-live li:last span').prop('className')). + toMatch(/even/); + }); + +
    + */ +var ngClassEvenDirective = classDirective('Even', 1); + +/** + * @ngdoc directive + * @name ng.directive:ngCloak + * + * @description + * The `ngCloak` directive is used to prevent the Angular html template from being briefly + * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this + * directive to avoid the undesirable flicker effect caused by the html template display. + * + * The directive can be applied to the `` element, but typically a fine-grained application is + * prefered in order to benefit from progressive rendering of the browser view. + * + * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and + * `angular.min.js` files. Following is the css rule: + * + *
    + * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
    + *   display: none;
    + * }
    + * 
    + * + * When this css rule is loaded by the browser, all html elements (including their children) that + * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive + * during the compilation of the template it deletes the `ngCloak` element attribute, which + * makes the compiled element visible. + * + * For the best result, `angular.js` script must be loaded in the head section of the html file; + * alternatively, the css rule (above) must be included in the external stylesheet of the + * application. + * + * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they + * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css + * class `ngCloak` in addition to `ngCloak` directive as shown in the example below. + * + * @element ANY + * + * @example + + +
    {{ 'hello' }}
    +
    {{ 'hello IE7' }}
    +
    + + it('should remove the template directive and css class', function() { + expect(element('.doc-example-live #template1').attr('ng-cloak')). + not().toBeDefined(); + expect(element('.doc-example-live #template2').attr('ng-cloak')). + not().toBeDefined(); + }); + +
    + * + */ +var ngCloakDirective = ngDirective({ + compile: function(element, attr) { + attr.$set('ngCloak', undefined); + element.removeClass('ng-cloak'); + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngController + * + * @description + * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular + * supports the principles behind the Model-View-Controller design pattern. + * + * MVC components in angular: + * + * * Model — The Model is data in scope properties; scopes are attached to the DOM. + * * View — The template (HTML with data bindings) is rendered into the View. + * * Controller — The `ngController` directive specifies a Controller class; the class has + * methods that typically express the business logic behind the application. + * + * Note that an alternative way to define controllers is via the {@link ng.$route $route} service. + * + * @element ANY + * @scope + * @param {expression} ngController Name of a globally accessible constructor function or an + * {@link guide/expression expression} that on the current scope evaluates to a + * constructor function. + * + * @example + * Here is a simple form for editing user contact information. Adding, removing, clearing, and + * greeting are methods declared on the controller (see source tab). These methods can + * easily be called from the angular markup. Notice that the scope becomes the `this` for the + * controller's instance. This allows for easy access to the view data from the controller. Also + * notice that any changes to the data are automatically reflected in the View without the need + * for a manual update. + + + +
    + Name: + [ greet ]
    + Contact: +
      +
    • + + + [ clear + | X ] +
    • +
    • [ add ]
    • +
    +
    +
    + + it('should check controller', function() { + expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); + expect(element('.doc-example-live li:nth-child(1) input').val()) + .toBe('408 555 1212'); + expect(element('.doc-example-live li:nth-child(2) input').val()) + .toBe('john.smith@example.org'); + + element('.doc-example-live li:first a:contains("clear")').click(); + expect(element('.doc-example-live li:first input').val()).toBe(''); + + element('.doc-example-live li:last a:contains("add")').click(); + expect(element('.doc-example-live li:nth-child(3) input').val()) + .toBe('yourname@example.org'); + }); + +
    + */ +var ngControllerDirective = [function() { + return { + scope: true, + controller: '@' + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngCsp + * @priority 1000 + * + * @element html + * @description + * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * This is necessary when developing things like Google Chrome Extensions. + * + * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). + * For us to be compatible, we just need to implement the "getterFn" in $parse without violating + * any of these restrictions. + * + * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp` + * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will + * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will + * be raised. + * + * In order to use this feature put `ngCsp` directive on the root element of the application. + * + * @example + * This example shows how to apply the `ngCsp` directive to the `html` tag. +
    +     
    +     
    +     ...
    +     ...
    +     
    +   
    + */ + +var ngCspDirective = ['$sniffer', function($sniffer) { + return { + priority: 1000, + compile: function() { + $sniffer.csp = true; + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngClick + * + * @description + * The ngClick allows you to specify custom behavior when + * element is clicked. + * + * @element ANY + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon + * click. (Event object is available as `$event`) + * + * @example + + + + count: {{count}} + + + it('should check ng-click', function() { + expect(binding('count')).toBe('0'); + element('.doc-example-live :button').click(); + expect(binding('count')).toBe('1'); + }); + + + */ +/* + * A directive that allows creation of custom onclick handlers that are defined as angular + * expressions and are compiled and executed within the current scope. + * + * Events that are handled via these handler are always configured not to propagate further. + */ +var ngEventDirectives = {}; +forEach( + 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '), + function(name) { + var directiveName = directiveNormalize('ng-' + name); + ngEventDirectives[directiveName] = ['$parse', function($parse) { + return function(scope, element, attr) { + var fn = $parse(attr[directiveName]); + element.bind(lowercase(name), function(event) { + scope.$apply(function() { + fn(scope, {$event:event}); + }); + }); + }; + }]; + } +); + +/** + * @ngdoc directive + * @name ng.directive:ngDblclick + * + * @description + * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. + * + * @element ANY + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon + * dblclick. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMousedown + * + * @description + * The ngMousedown directive allows you to specify custom behavior on mousedown event. + * + * @element ANY + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon + * mousedown. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseup + * + * @description + * Specify custom behavior on mouseup event. + * + * @element ANY + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon + * mouseup. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ng.directive:ngMouseover + * + * @description + * Specify custom behavior on mouseover event. + * + * @element ANY + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon + * mouseover. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseenter + * + * @description + * Specify custom behavior on mouseenter event. + * + * @element ANY + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon + * mouseenter. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMouseleave + * + * @description + * Specify custom behavior on mouseleave event. + * + * @element ANY + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon + * mouseleave. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngMousemove + * + * @description + * Specify custom behavior on mousemove event. + * + * @element ANY + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon + * mousemove. (Event object is available as `$event`) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + + +/** + * @ngdoc directive + * @name ng.directive:ngSubmit + * + * @description + * Enables binding angular expressions to onsubmit events. + * + * Additionally it prevents the default action (which for form means sending the request to the + * server and reloading the current page). + * + * @element form + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. + * + * @example + + + +
    + Enter text and hit enter: + + +
    list={{list}}
    +
    +
    + + it('should check ng-submit', function() { + expect(binding('list')).toBe('[]'); + element('.doc-example-live #submit').click(); + expect(binding('list')).toBe('["hello"]'); + expect(input('text').val()).toBe(''); + }); + it('should ignore empty strings', function() { + expect(binding('list')).toBe('[]'); + element('.doc-example-live #submit').click(); + element('.doc-example-live #submit').click(); + expect(binding('list')).toBe('["hello"]'); + }); + +
    + */ +var ngSubmitDirective = ngDirective(function(scope, element, attrs) { + element.bind('submit', function() { + scope.$apply(attrs.ngSubmit); + }); +}); + +/** + * @ngdoc directive + * @name ng.directive:ngInclude + * @restrict ECA + * + * @description + * Fetches, compiles and includes an external HTML fragment. + * + * Keep in mind that Same Origin Policy applies to included resources + * (e.g. ngInclude won't work for cross-domain requests on all browsers and for + * file:// access on some browsers). + * + * @scope + * + * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, + * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`. + * @param {string=} onload Expression to evaluate when a new partial is loaded. + * + * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll + * $anchorScroll} to scroll the viewport after the content is loaded. + * + * - If the attribute is not set, disable scrolling. + * - If the attribute is set without value, enable scrolling. + * - Otherwise enable scrolling only if the expression evaluates to truthy value. + * + * @example + + +
    + + url of the template: {{template.url}} +
    +
    +
    +
    + + function Ctrl($scope) { + $scope.templates = + [ { name: 'template1.html', url: 'template1.html'} + , { name: 'template2.html', url: 'template2.html'} ]; + $scope.template = $scope.templates[0]; + } + + + Content of template1.html + + + Content of template2.html + + + it('should load template1.html', function() { + expect(element('.doc-example-live [ng-include]').text()). + toMatch(/Content of template1.html/); + }); + it('should load template2.html', function() { + select('template').option('1'); + expect(element('.doc-example-live [ng-include]').text()). + toMatch(/Content of template2.html/); + }); + it('should change to blank', function() { + select('template').option(''); + expect(element('.doc-example-live [ng-include]').text()).toEqual(''); + }); + +
    + */ + + +/** + * @ngdoc event + * @name ng.directive:ngInclude#$includeContentLoaded + * @eventOf ng.directive:ngInclude + * @eventType emit on the current ngInclude scope + * @description + * Emitted every time the ngInclude content is reloaded. + */ +var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', + function($http, $templateCache, $anchorScroll, $compile) { + return { + restrict: 'ECA', + terminal: true, + compile: function(element, attr) { + var srcExp = attr.ngInclude || attr.src, + onloadExp = attr.onload || '', + autoScrollExp = attr.autoscroll; + + return function(scope, element) { + var changeCounter = 0, + childScope; + + var clearContent = function() { + if (childScope) { + childScope.$destroy(); + childScope = null; + } + + element.html(''); + }; + + scope.$watch(srcExp, function ngIncludeWatchAction(src) { + var thisChangeId = ++changeCounter; + + if (src) { + $http.get(src, {cache: $templateCache}).success(function(response) { + if (thisChangeId !== changeCounter) return; + + if (childScope) childScope.$destroy(); + childScope = scope.$new(); + + element.html(response); + $compile(element.contents())(childScope); + + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + + childScope.$emit('$includeContentLoaded'); + scope.$eval(onloadExp); + }).error(function() { + if (thisChangeId === changeCounter) clearContent(); + }); + } else clearContent(); + }); + }; + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngInit + * + * @description + * The `ngInit` directive specifies initialization tasks to be executed + * before the template enters execution mode during bootstrap. + * + * @element ANY + * @param {expression} ngInit {@link guide/expression Expression} to eval. + * + * @example + + +
    + {{greeting}} {{person}}! +
    +
    + + it('should check greeting', function() { + expect(binding('greeting')).toBe('Hello'); + expect(binding('person')).toBe('World'); + }); + +
    + */ +var ngInitDirective = ngDirective({ + compile: function() { + return { + pre: function(scope, element, attrs) { + scope.$eval(attrs.ngInit); + } + } + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngNonBindable + * @priority 1000 + * + * @description + * Sometimes it is necessary to write code which looks like bindings but which should be left alone + * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML. + * + * @element ANY + * + * @example + * In this example there are two location where a simple binding (`{{}}`) is present, but the one + * wrapped in `ngNonBindable` is left alone. + * + * @example + + +
    Normal: {{1 + 2}}
    +
    Ignored: {{1 + 2}}
    +
    + + it('should check ng-non-bindable', function() { + expect(using('.doc-example-live').binding('1 + 2')).toBe('3'); + expect(using('.doc-example-live').element('div:last').text()). + toMatch(/1 \+ 2/); + }); + +
    + */ +var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); + +/** + * @ngdoc directive + * @name ng.directive:ngPluralize + * @restrict EA + * + * @description + * # Overview + * `ngPluralize` is a directive that displays messages according to en-US localization rules. + * These rules are bundled with angular.js and the rules can be overridden + * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive + * by specifying the mappings between + * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * plural categories} and the strings to be displayed. + * + * # Plural categories and explicit number rules + * There are two + * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * plural categories} in Angular's default en-US locale: "one" and "other". + * + * While a pural category may match many numbers (for example, in en-US locale, "other" can match + * any number that is not 1), an explicit number rule can only match one number. For example, the + * explicit number rule for "3" matches the number 3. You will see the use of plural categories + * and explicit number rules throughout later parts of this documentation. + * + * # Configuring ngPluralize + * You configure ngPluralize by providing 2 attributes: `count` and `when`. + * You can also provide an optional attribute, `offset`. + * + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. + * + * The `when` attribute specifies the mappings between plural categories and the actual + * string to be displayed. The value of the attribute should be a JSON object so that Angular + * can interpret it correctly. + * + * The following example shows how to configure ngPluralize: + * + *
    + * 
    + * 
    + *
    + * + * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not + * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" + * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for + * other numbers, for example 12, so that instead of showing "12 people are viewing", you can + * show "a dozen people are viewing". + * + * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted + * into pluralized strings. In the previous example, Angular will replace `{}` with + * `{{personCount}}`. The closed braces `{}` is a placeholder + * for {{numberExpression}}. + * + * # Configuring ngPluralize with offset + * The `offset` attribute allows further customization of pluralized text, which can result in + * a better user experience. For example, instead of the message "4 people are viewing this document", + * you might display "John, Kate and 2 others are viewing this document". + * The offset attribute allows you to offset a number by any desired value. + * Let's take a look at an example: + * + *
    + * 
    + * 
    + * 
    + * + * Notice that we are still using two plural categories(one, other), but we added + * three explicit number rules 0, 1 and 2. + * When one person, perhaps John, views the document, "John is viewing" will be shown. + * When three people view the document, no explicit number rule is found, so + * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. + * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing" + * is shown. + * + * Note that when you specify offsets, you must provide explicit number rules for + * numbers from 0 up to and including the offset. If you use an offset of 3, for example, + * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for + * plural categories "one" and "other". + * + * @param {string|expression} count The variable to be bounded to. + * @param {string} when The mapping between plural category to its correspoding strings. + * @param {number=} offset Offset to deduct from the total number. + * + * @example + + + +
    + Person 1:
    + Person 2:
    + Number of People:
    + + + Without Offset: + +
    + + + With Offset(2): + + +
    +
    + + it('should show correct pluralized string', function() { + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('1 person is viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor is viewing.'); + + using('.doc-example-live').input('personCount').enter('0'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('Nobody is viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Nobody is viewing.'); + + using('.doc-example-live').input('personCount').enter('2'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('2 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor and Misko are viewing.'); + + using('.doc-example-live').input('personCount').enter('3'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('3 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and one other person are viewing.'); + + using('.doc-example-live').input('personCount').enter('4'); + expect(element('.doc-example-live ng-pluralize:first').text()). + toBe('4 people are viewing.'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and 2 other people are viewing.'); + }); + + it('should show data-binded names', function() { + using('.doc-example-live').input('personCount').enter('4'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Igor, Misko and 2 other people are viewing.'); + + using('.doc-example-live').input('person1').enter('Di'); + using('.doc-example-live').input('person2').enter('Vojta'); + expect(element('.doc-example-live ng-pluralize:last').text()). + toBe('Di, Vojta and 2 other people are viewing.'); + }); + +
    + */ +var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { + var BRACE = /{}/g; + return { + restrict: 'EA', + link: function(scope, element, attr) { + var numberExp = attr.count, + whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs + offset = attr.offset || 0, + whens = scope.$eval(whenExp), + whensExpFns = {}, + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(); + + forEach(whens, function(expression, key) { + whensExpFns[key] = + $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + + offset + endSymbol)); + }); + + scope.$watch(function ngPluralizeWatch() { + var value = parseFloat(scope.$eval(numberExp)); + + if (!isNaN(value)) { + //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, + //check it against pluralization rules in $locale service + if (!(value in whens)) value = $locale.pluralCat(value - offset); + return whensExpFns[value](scope, element, true); + } else { + return ''; + } + }, function ngPluralizeWatchAction(newVal) { + element.text(newVal); + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:ngRepeat + * + * @description + * The `ngRepeat` directive instantiates a template once per item from a collection. Each template + * instance gets its own scope, where the given loop variable is set to the current collection item, + * and `$index` is set to the item index or key. + * + * Special properties are exposed on the local scope of each template instance, including: + * + * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1) + * * `$first` – `{boolean}` – true if the repeated element is first in the iterator. + * * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator. + * * `$last` – `{boolean}` – true if the repeated element is last in the iterator. + * + * + * @element ANY + * @scope + * @priority 1000 + * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two + * formats are currently supported: + * + * * `variable in expression` – where variable is the user defined loop variable and `expression` + * is a scope expression giving the collection to enumerate. + * + * For example: `track in cd.tracks`. + * + * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, + * and `expression` is the scope expression giving the collection to enumerate. + * + * For example: `(name, age) in {'adam':10, 'amalie':12}`. + * + * @example + * This example initializes the scope to a list of names and + * then uses `ngRepeat` to display every person: + + +
    + I have {{friends.length}} friends. They are: +
      +
    • + [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. +
    • +
    +
    +
    + + it('should check ng-repeat', function() { + var r = using('.doc-example-live').repeater('ul li'); + expect(r.count()).toBe(2); + expect(r.row(0)).toEqual(["1","John","25"]); + expect(r.row(1)).toEqual(["2","Mary","28"]); + }); + +
    + */ +var ngRepeatDirective = ngDirective({ + transclude: 'element', + priority: 1000, + terminal: true, + compile: function(element, attr, linker) { + return function(scope, iterStartElement, attr){ + var expression = attr.ngRepeat; + var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), + lhs, rhs, valueIdent, keyIdent; + if (! match) { + throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '" + + expression + "'."); + } + lhs = match[1]; + rhs = match[2]; + match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); + if (!match) { + throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + + lhs + "'."); + } + valueIdent = match[3] || match[1]; + keyIdent = match[2]; + + // Store a list of elements from previous run. This is a hash where key is the item from the + // iterator, and the value is an array of objects with following properties. + // - scope: bound scope + // - element: previous element. + // - index: position + // We need an array of these objects since the same object can be returned from the iterator. + // We expect this to be a rare case. + var lastOrder = new HashQueueMap(); + + scope.$watch(function ngRepeatWatch(scope){ + var index, length, + collection = scope.$eval(rhs), + cursor = iterStartElement, // current position of the node + // Same as lastOrder but it has the current state. It will become the + // lastOrder on the next iteration. + nextOrder = new HashQueueMap(), + arrayBound, + childScope, + key, value, // key/value of iteration + array, + last; // last object information {scope, element, index} + + + + if (!isArray(collection)) { + // if object, extract keys, sort them and use to determine order of iteration over obj props + array = []; + for(key in collection) { + if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { + array.push(key); + } + } + array.sort(); + } else { + array = collection || []; + } + + arrayBound = array.length-1; + + // we are not using forEach for perf reasons (trying to avoid #call) + for (index = 0, length = array.length; index < length; index++) { + key = (collection === array) ? index : array[index]; + value = collection[key]; + + last = lastOrder.shift(value); + + if (last) { + // if we have already seen this object, then we need to reuse the + // associated scope/element + childScope = last.scope; + nextOrder.push(value, last); + + if (index === last.index) { + // do nothing + cursor = last.element; + } else { + // existing item which got moved + last.index = index; + // This may be a noop, if the element is next, but I don't know of a good way to + // figure this out, since it would require extra DOM access, so let's just hope that + // the browsers realizes that it is noop, and treats it as such. + cursor.after(last.element); + cursor = last.element; + } + } else { + // new item which we don't know about + childScope = scope.$new(); + } + + childScope[valueIdent] = value; + if (keyIdent) childScope[keyIdent] = key; + childScope.$index = index; + + childScope.$first = (index === 0); + childScope.$last = (index === arrayBound); + childScope.$middle = !(childScope.$first || childScope.$last); + + if (!last) { + linker(childScope, function(clone){ + cursor.after(clone); + last = { + scope: childScope, + element: (cursor = clone), + index: index + }; + nextOrder.push(value, last); + }); + } + } + + //shrink children + for (key in lastOrder) { + if (lastOrder.hasOwnProperty(key)) { + array = lastOrder[key]; + while(array.length) { + value = array.pop(); + value.element.remove(); + value.scope.$destroy(); + } + } + } + + lastOrder = nextOrder; + }); + }; + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngShow + * + * @description + * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) + * conditionally. + * + * @element ANY + * @param {expression} ngShow If the {@link guide/expression expression} is truthy + * then the element is shown or hidden respectively. + * + * @example + + + Click me:
    + Show: I show up when your checkbox is checked.
    + Hide: I hide when your checkbox is checked. +
    + + it('should check ng-show / ng-hide', function() { + expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); + expect(element('.doc-example-live span:last:visible').count()).toEqual(1); + + input('checked').check(); + + expect(element('.doc-example-live span:first:visible').count()).toEqual(1); + expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); + }); + +
    + */ +//TODO(misko): refactor to remove element from the DOM +var ngShowDirective = ngDirective(function(scope, element, attr){ + scope.$watch(attr.ngShow, function ngShowWatchAction(value){ + element.css('display', toBoolean(value) ? '' : 'none'); + }); +}); + + +/** + * @ngdoc directive + * @name ng.directive:ngHide + * + * @description + * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML) + * conditionally. + * + * @element ANY + * @param {expression} ngHide If the {@link guide/expression expression} is truthy then + * the element is shown or hidden respectively. + * + * @example + + + Click me:
    + Show: I show up when you checkbox is checked?
    + Hide: I hide when you checkbox is checked? +
    + + it('should check ng-show / ng-hide', function() { + expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); + expect(element('.doc-example-live span:last:visible').count()).toEqual(1); + + input('checked').check(); + + expect(element('.doc-example-live span:first:visible').count()).toEqual(1); + expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); + }); + +
    + */ +//TODO(misko): refactor to remove element from the DOM +var ngHideDirective = ngDirective(function(scope, element, attr){ + scope.$watch(attr.ngHide, function ngHideWatchAction(value){ + element.css('display', toBoolean(value) ? 'none' : ''); + }); +}); + +/** + * @ngdoc directive + * @name ng.directive:ngStyle + * + * @description + * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. + * + * @element ANY + * @param {expression} ngStyle {@link guide/expression Expression} which evals to an + * object whose keys are CSS style names and values are corresponding values for those CSS + * keys. + * + * @example + + + + +
    + Sample Text +
    myStyle={{myStyle}}
    +
    + + span { + color: black; + } + + + it('should check ng-style', function() { + expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); + element('.doc-example-live :button[value=set]').click(); + expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)'); + element('.doc-example-live :button[value=clear]').click(); + expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); + }); + +
    + */ +var ngStyleDirective = ngDirective(function(scope, element, attr) { + scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { + if (oldStyles && (newStyles !== oldStyles)) { + forEach(oldStyles, function(val, style) { element.css(style, '');}); + } + if (newStyles) element.css(newStyles); + }, true); +}); + +/** + * @ngdoc directive + * @name ng.directive:ngSwitch + * @restrict EA + * + * @description + * Conditionally change the DOM structure. + * + * @usage + * + * ... + * ... + * ... + * ... + * + * + * @scope + * @param {*} ngSwitch|on expression to match against ng-switch-when. + * @paramDescription + * On child elments add: + * + * * `ngSwitchWhen`: the case statement to match against. If match then this + * case will be displayed. + * * `ngSwitchDefault`: the default case when no other casses match. + * + * @example + + + +
    + + selection={{selection}} +
    +
    +
    Settings Div
    + Home Span + default +
    +
    +
    + + it('should start in settings', function() { + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/); + }); + it('should change to home', function() { + select('selection').option('home'); + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/); + }); + it('should select deafault', function() { + select('selection').option('other'); + expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/); + }); + +
    + */ +var NG_SWITCH = 'ng-switch'; +var ngSwitchDirective = valueFn({ + restrict: 'EA', + require: 'ngSwitch', + // asks for $scope to fool the BC controller module + controller: ['$scope', function ngSwitchController() { + this.cases = {}; + }], + link: function(scope, element, attr, ctrl) { + var watchExpr = attr.ngSwitch || attr.on, + selectedTransclude, + selectedElement, + selectedScope; + + scope.$watch(watchExpr, function ngSwitchWatchAction(value) { + if (selectedElement) { + selectedScope.$destroy(); + selectedElement.remove(); + selectedElement = selectedScope = null; + } + if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) { + scope.$eval(attr.change); + selectedScope = scope.$new(); + selectedTransclude(selectedScope, function(caseElement) { + selectedElement = caseElement; + element.append(caseElement); + }); + } + }); + } +}); + +var ngSwitchWhenDirective = ngDirective({ + transclude: 'element', + priority: 500, + require: '^ngSwitch', + compile: function(element, attrs, transclude) { + return function(scope, element, attr, ctrl) { + ctrl.cases['!' + attrs.ngSwitchWhen] = transclude; + }; + } +}); + +var ngSwitchDefaultDirective = ngDirective({ + transclude: 'element', + priority: 500, + require: '^ngSwitch', + compile: function(element, attrs, transclude) { + return function(scope, element, attr, ctrl) { + ctrl.cases['?'] = transclude; + }; + } +}); + +/** + * @ngdoc directive + * @name ng.directive:ngTransclude + * + * @description + * Insert the transcluded DOM here. + * + * @element ANY + * + * @example + + + +
    +
    +
    + {{text}} +
    +
    + + it('should have transcluded', function() { + input('title').enter('TITLE'); + input('text').enter('TEXT'); + expect(binding('title')).toEqual('TITLE'); + expect(binding('text')).toEqual('TEXT'); + }); + +
    + * + */ +var ngTranscludeDirective = ngDirective({ + controller: ['$transclude', '$element', function($transclude, $element) { + $transclude(function(clone) { + $element.append(clone); + }); + }] +}); + +/** + * @ngdoc directive + * @name ng.directive:ngView + * @restrict ECA + * + * @description + * # Overview + * `ngView` is a directive that complements the {@link ng.$route $route} service by + * including the rendered template of the current route into the main layout (`index.html`) file. + * Every time the current route changes, the included view changes with it according to the + * configuration of the `$route` service. + * + * @scope + * @example + + +
    + Choose: + Moby | + Moby: Ch1 | + Gatsby | + Gatsby: Ch4 | + Scarlet Letter
    + +
    +
    + +
    $location.path() = {{$location.path()}}
    +
    $route.current.templateUrl = {{$route.current.templateUrl}}
    +
    $route.current.params = {{$route.current.params}}
    +
    $route.current.scope.name = {{$route.current.scope.name}}
    +
    $routeParams = {{$routeParams}}
    +
    +
    + + + controller: {{name}}
    + Book Id: {{params.bookId}}
    +
    + + + controller: {{name}}
    + Book Id: {{params.bookId}}
    + Chapter Id: {{params.chapterId}} +
    + + + angular.module('ngView', [], function($routeProvider, $locationProvider) { + $routeProvider.when('/Book/:bookId', { + templateUrl: 'book.html', + controller: BookCntl + }); + $routeProvider.when('/Book/:bookId/ch/:chapterId', { + templateUrl: 'chapter.html', + controller: ChapterCntl + }); + + // configure html5 to get links working on jsfiddle + $locationProvider.html5Mode(true); + }); + + function MainCntl($scope, $route, $routeParams, $location) { + $scope.$route = $route; + $scope.$location = $location; + $scope.$routeParams = $routeParams; + } + + function BookCntl($scope, $routeParams) { + $scope.name = "BookCntl"; + $scope.params = $routeParams; + } + + function ChapterCntl($scope, $routeParams) { + $scope.name = "ChapterCntl"; + $scope.params = $routeParams; + } + + + + it('should load and compile correct template', function() { + element('a:contains("Moby: Ch1")').click(); + var content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: ChapterCntl/); + expect(content).toMatch(/Book Id\: Moby/); + expect(content).toMatch(/Chapter Id\: 1/); + + element('a:contains("Scarlet")').click(); + content = element('.doc-example-live [ng-view]').text(); + expect(content).toMatch(/controller\: BookCntl/); + expect(content).toMatch(/Book Id\: Scarlet/); + }); + +
    + */ + + +/** + * @ngdoc event + * @name ng.directive:ngView#$viewContentLoaded + * @eventOf ng.directive:ngView + * @eventType emit on the current ngView scope + * @description + * Emitted every time the ngView content is reloaded. + */ +var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', + '$controller', + function($http, $templateCache, $route, $anchorScroll, $compile, + $controller) { + return { + restrict: 'ECA', + terminal: true, + link: function(scope, element, attr) { + var lastScope, + onloadExp = attr.onload || ''; + + scope.$on('$routeChangeSuccess', update); + update(); + + + function destroyLastScope() { + if (lastScope) { + lastScope.$destroy(); + lastScope = null; + } + } + + function clearContent() { + element.html(''); + destroyLastScope(); + } + + function update() { + var locals = $route.current && $route.current.locals, + template = locals && locals.$template; + + if (template) { + element.html(template); + destroyLastScope(); + + var link = $compile(element.contents()), + current = $route.current, + controller; + + lastScope = current.scope = scope.$new(); + if (current.controller) { + locals.$scope = lastScope; + controller = $controller(current.controller, locals); + element.children().data('$ngControllerController', controller); + } + + link(lastScope); + lastScope.$emit('$viewContentLoaded'); + lastScope.$eval(onloadExp); + + // $anchorScroll might listen on event... + $anchorScroll(); + } else { + clearContent(); + } + } + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:script + * + * @description + * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the + * template can be used by `ngInclude`, `ngView` or directive templates. + * + * @restrict E + * @param {'text/ng-template'} type must be set to `'text/ng-template'` + * + * @example + + + + + Load inlined template +
    +
    + + it('should load template defined inside script tag', function() { + element('#tpl-link').click(); + expect(element('#tpl-content').text()).toMatch(/Content of the template/); + }); + +
    + */ +var scriptDirective = ['$templateCache', function($templateCache) { + return { + restrict: 'E', + terminal: true, + compile: function(element, attr) { + if (attr.type == 'text/ng-template') { + var templateUrl = attr.id, + // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent + text = element[0].text; + + $templateCache.put(templateUrl, text); + } + } + }; +}]; + +/** + * @ngdoc directive + * @name ng.directive:select + * @restrict E + * + * @description + * HTML `SELECT` element with angular data-binding. + * + * # `ngOptions` + * + * Optionally `ngOptions` attribute can be used to dynamically generate a list of `
  • 苹果