Commit ccbaa06e7f43a8822849876066a9ddc29445339d

Authored by pengye
2 parents a91fc7d2 a988d490

Merge branch 'master' of http://123.56.79.57/najixian/xt_dc_admin

Showing 45 changed files with 7034 additions and 28 deletions
@@ -57,6 +57,265 @@ class System extends Common @@ -57,6 +57,265 @@ class System extends Common
57 * 获取日志列表 57 * 获取日志列表
58 */ 58 */
59 public function log(){ 59 public function log(){
  60 + $identity = Db::query("SELECT identity FROM `xt-member-two-LINE`.xt_dc_dz_log_action GROUP BY identity");
  61 + if(Request::isAjax()){
  62 + $data = Request::except('file');
  63 + $key = input('post.key') ? input('post.key') : "";
  64 +
  65 + $key = $data['key'] ? " a.`name` LIKE '%".$data['key']."%'" : '1=1';
  66 +
  67 + $nickname = $data['nickname'] ? " AND a.nickname LIKE '%".$data['nickname']."%'" : '';
  68 +
  69 + $data_time= $data['time'] ? explode(' - ', $data['time']) : array(date("Y-m-d 00:00:00"),date("Y-m-d H:i:s"));
  70 + $time = $data_time ? "AND a.cdate BETWEEN '".$data_time[0]."' AND '".$data_time[1]."'" : '';
  71 +
  72 + $iden = ($data['iden'] && $data['iden']!=-1) ? " AND a.identity = ".$data['iden'] : "";
  73 +
  74 + $channel = ($data['channel'] && $data['channel']!=-1) ? " AND rel.type_id = ".$data['channel'] : "";
  75 +
  76 + $log_info = Db::query("SELECT
  77 + a.*,
  78 + h.field,
  79 + h.old,
  80 + h.new,
  81 + h.differ,
  82 + rel.type_id
  83 + FROM
  84 + `xt-member-two-LINE`.xt_dc_dz_log_action AS a
  85 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
  86 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
  87 + WHERE
  88 + $key
  89 + $nickname
  90 + $iden
  91 + $channel
  92 + $time
  93 + order by
  94 + a.cdate desc
  95 + LIMIT ".$data['page'].",".$data['limit']."");
  96 + $total = Db::query("SELECT
  97 + count(1) as total
  98 + FROM
  99 + `xt-member-two-LINE`.xt_dc_dz_log_action AS a
  100 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
  101 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
  102 + WHERE
  103 + $key
  104 + $nickname
  105 + $iden
  106 + $channel
  107 + $time");
  108 + foreach ($log_info as $key => $value) {
  109 + switch ($value['device']) {
  110 + case '1':
  111 + $log_info[$key]['device'] = '小程序(C端)';
  112 + break;
  113 + case '2':
  114 + $log_info[$key]['device'] = '小程序(B端)';
  115 + break;
  116 + case '3':
  117 + $log_info[$key]['device'] = '铱方';
  118 + break;
  119 + case '4':
  120 + $log_info[$key]['device'] = '极钛';
  121 + break;
  122 + case '5':
  123 + $log_info[$key]['device'] = '酷比';
  124 + break;
  125 + case '6':
  126 + $log_info[$key]['device'] = '华为';
  127 + break;
  128 + default:
  129 + # code...
  130 + break;
  131 + }
  132 + switch ($value['field']) {
  133 + case 'status':
  134 + $log_info[$key]['old'] = lang('log')[$value['old']];
  135 + $log_info[$key]['new'] = lang('log')[$value['new']];
  136 + break;
  137 + case 'ddate':
  138 + $log_info[$key]['old'] = date("Y-m-d H:i:s",$value['old']);
  139 + $log_info[$key]['new'] = date("Y-m-d H:i:s",$value['new']);
  140 + break;
  141 + case 'action':
  142 + $log_info[$key]['old'] = $value['old']==0 ? '未发短信' : ($value['old']==1 ? '已发短信' : '已回访');
  143 + $log_info[$key]['new'] = $value['new']==0 ? '未发短信' : ($value['new']==1 ? '已发短信' : '已回访');
  144 + break;
  145 + default:
  146 + # code...
  147 + break;
  148 + }
  149 + //跳转页面逻辑判断
  150 + //规则:
  151 + //1、如果field字段存在 则直接显示变更前,变更后
  152 + //2、如果预定ID为0
  153 + $haystack = array('Android.Reserve.SetReserveInfo','App.Customer.SetReserveInfo');
  154 + if(array_key_exists($value['field'],lang('log'))){
  155 + $log_info[$key]['field'] = lang('log')[$value['field']];
  156 + }elseif($value['rid']==0){
  157 + $log_info[$key]['field'] = lang('log')['seller'];
  158 + }elseif(in_array($value['action'], $haystack)){
  159 + if($value['comment']){
  160 + $log_info[$key]['field'] = lang('log')['add_rid'];
  161 + $log_info[$key]['old'] = '<a href=/admin/system/detail?id='.$value["id"].'&tag=1 target="_blank">查看详情</a>';
  162 + $log_info[$key]['new'] = '<a href=/admin/system/detail?id='.$value["id"].'&tag=1 target="_blank">查看详情</a>';
  163 + }else{
  164 + $log_info[$key]['field'] = lang('log')['edi_rid'];
  165 + $log_info[$key]['old'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
  166 + $log_info[$key]['new'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
  167 + }
  168 + }else{
  169 + $log_info[$key]['field'] = lang('log')['edi_rid'];
  170 + $log_info[$key]['old'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
  171 + $log_info[$key]['new'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
  172 + }
  173 + }
  174 + return ['code'=>0,'msg'=>'获取成功!','data'=>$log_info,'count'=>$total[0]['total'],'rel'=>1];
  175 + }
  176 +
  177 + $idn = array();
  178 + foreach (array_column($identity, 'identity') as $key => $value) {
  179 + switch ($value) {
  180 + case '0':
  181 + $idn[$value] = '顾客';
  182 + break;
  183 + case '1':
  184 + $idn[$value] = '运营';
  185 + break;
  186 + case '2':
  187 + $idn[$value] = '代理商';
  188 + break;
  189 + case '3':
  190 + $idn[$value] = '商家人员';
  191 + break;
  192 + default:
  193 + # code...
  194 + break;
  195 + }
  196 + }
  197 + $tmp = array();
  198 + while (list($key, $val) = each($idn)) {
  199 + $tmp[] = array('key'=>$key,'val'=>$val);
  200 + }
  201 + $this->assign('identity', $tmp);
  202 + return $this->fetch();
  203 + }
  204 +
  205 + public function differ(){
  206 + $data = Request::except('file');
  207 + if($data['id']){
  208 + $log_info = Db::query("SELECT
  209 + a.*,
  210 + h.field,
  211 + h.old,
  212 + h.new,
  213 + h.differ,
  214 + rel.type_id
  215 + FROM
  216 + `xt-member-two-LINE`.xt_dc_dz_log_action AS a
  217 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
  218 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
  219 + WHERE
  220 + a.id = ".$data['id']);
  221 + $log_info = $log_info[0];
  222 + if($log_info['comment']){
  223 + $log_info['comment'] = json_decode($log_info['comment'],true);
  224 + if($log_info['comment']['room_demand']){
  225 + $log_info['comment']['room_demand'] = json_decode($log_info['comment']['room_demand'],true);
  226 + }
  227 + }
  228 + if($log_info['old']){
  229 + $log_info['old'] = json_decode($log_info['old'],true);
  230 + if($log_info['old']['tableinfo']){
  231 + $log_info['old']['tableinfo'] = json_decode($log_info['old']['tableinfo'],true);
  232 + }
  233 + }
  234 + if($log_info['new']){
  235 + $log_info['new'] = json_decode($log_info['new'],true);
  236 + if($log_info['new']['tableinfo']){
  237 + $log_info['new']['tableinfo'] = json_decode($log_info['new']['tableinfo'],true);
  238 + }
  239 + }
  240 + $old = json_encode($log_info['old'],JSON_UNESCAPED_UNICODE);
  241 + $new = json_encode($log_info['new'],JSON_UNESCAPED_UNICODE);
  242 + }
  243 + $this->assign('html', $log_info);
  244 + $this->assign('new', $new);
  245 + $this->assign('old', $old);
  246 + return $this->fetch();
  247 + }
  248 +
  249 + public function detail(){
  250 + $data = Request::except('file');
  251 + if($data['id']){
  252 + $log_info = Db::query("SELECT
  253 + a.*,
  254 + h.field,
  255 + h.old,
  256 + h.new,
  257 + h.differ,
  258 + rel.type_id
  259 + FROM
  260 + `xt-member-two-LINE`.xt_dc_dz_log_action AS a
  261 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
  262 + LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
  263 + WHERE
  264 + a.id = ".$data['id']);
  265 + $log_info = $log_info[0];
  266 + if($log_info['comment']){
  267 + $log_info['comment'] = json_decode($log_info['comment'],true);
  268 + if($log_info['comment']['row']['tableinfo']){
  269 + $log_info['comment']['row']['tableinfo'] = json_decode($log_info['comment']['row']['tableinfo'],true);
  270 + }
  271 + }
  272 + switch ($log_info['device']) {
  273 + case '1':
  274 + $log_info['device'] = '小程序(C端)';
  275 + break;
  276 + case '2':
  277 + $log_info['device'] = '小程序(B端)';
  278 + break;
  279 + case '3':
  280 + $log_info['device'] = '铱方';
  281 + break;
  282 + case '4':
  283 + $log_info['device'] = '极钛';
  284 + break;
  285 + case '5':
  286 + $log_info['device'] = '酷比';
  287 + break;
  288 + case '6':
  289 + $log_info['device'] = '华为';
  290 + break;
  291 + default:
  292 + # code...
  293 + break;
  294 + }
  295 + if(array_key_exists($log_info['action'],lang('log'))){
  296 + if($data['tag']==1){
  297 + $log_info['action'] = lang('log')[$log_info['action']][0];
  298 + }else{
  299 + $log_info['action'] = lang('log')[$log_info['action']][1];
  300 + }
  301 + }
  302 + $log_info['rname'] = $log_info['comment']['row']['rname'] ? $log_info['comment']['row']['rname'] : '';
  303 + $log_info['sex'] = $log_info['comment']['row']['rname']==1 ? '先生' : ($log_info['comment']['row']['rname']==2 ? '女士' : '公司');
  304 + $log_info['phone'] = $log_info['comment']['row']['phone'] ? $log_info['comment']['row']['phone'] : '';
  305 + $log_info['cusman'] = $log_info['comment']['row']['cusman'] ? $log_info['comment']['row']['cusman'] : '';
  306 + $log_info['ddate'] = $log_info['comment']['row']['ddate'] ? date("Y-m-d H:i:s",$log_info['comment']['row']['ddate']) : '';
  307 + $log_info['cdate'] = $log_info['comment']['row']['cdate'] ? date("Y-m-d H:i:s",$log_info['comment']['row']['cdate']) : '';
  308 + $log_info['status'] = lang('log')[$log_info['comment']['row']['status']];
  309 + $log_info['ddate_solt'] = $log_info['comment']['row']['ddate_solt'] ? $log_info['comment']['row']['ddate_solt'] : '';
  310 + $log_info['is_set_ddate'] = $log_info['comment']['row']['is_set_ddate'] ==1 ? '是' : '否';
  311 +
  312 + }
  313 + $this->assign('html', $log_info);
  314 + return $this->fetch();
  315 + }
  316 +
  317 + //阿里云日志log
  318 + public function aliyunlog(){
60 //基础参数 319 //基础参数
61 /* 使用autoloader类自动加载所有需要的PHP模块。注意使用合适的路径指向autoloader类所在文件*/ 320 /* 使用autoloader类自动加载所有需要的PHP模块。注意使用合适的路径指向autoloader类所在文件*/
62 $DOCUMENT_ROOT = str_replace('public','',$_SERVER['DOCUMENT_ROOT']); 321 $DOCUMENT_ROOT = str_replace('public','',$_SERVER['DOCUMENT_ROOT']);
1 <?php 1 <?php
2 return [ 2 return [
3 /*********** 日志记录 **********/ 3 /*********** 日志记录 **********/
  4 + 'log'=>array(
  5 + 'ddate' => '到店时间',
  6 + 'status' => '预定状态',
  7 + 'action' => '短信回访',
  8 + 'add_rid' => '新增预定',
  9 + 'edi_rid' => '修改预定',
  10 + 'App.Customer.SetReserveInfo' => '录入了一笔订单',
  11 + 'App.Customer.SetReserveInfoById' => '修改了一笔订单',
  12 + 'App.Customer.SetDdate' => '设置了到店时间',
  13 + 'App.Customer.SetReserveStatus' => '设置了预定状态',
  14 + 'App.Reserve.NewExportReserve' => '导出了一份预定',
  15 + 'App.Reserve.NewSetReserveInfo' => '录入了一笔订单',
  16 + 'App.Reserve.NewSetReserveInfo' => '修改了一笔订单',
  17 + 'App.Reserve.SetReserveCusman' => '设置了客户经理',
  18 + 'App.Reserve.SetReserveStatus' => '设置了预定状态',
  19 + 'App.Reserve.UpdateAction' => '回访了短信',
  20 + 'App.Seller.AddSeller' => '添加了一个商家',
  21 + 'App.Seller.setSellerById' => '修改了商家信息',
  22 + 'App.Seller.InSterAuthUser' => '移交了店铺',
  23 + 'Android.Customer.SetReserveInfo' => '修改了一笔订单',
  24 + 'Android.Customer.SetReserveStatus' => '设置了预定状态',
  25 + 'Android.Reserve.SetReserveInfo' => array('录入了一笔订单','修改了一笔订单'),
  26 + 'Android.Reserve.SetReserveStatus' => '设置了预定状态',
  27 + 'Android.Reserve.UpdateAction' => '回访了短信',
  28 + '0' => '待确认',
  29 + '1' => '已定座',
  30 + '2' => '确认到店',
  31 + '3' => '用餐完成',
  32 + '4' => '取消预定',
  33 + '5' => '分配包间',
  34 + '6' => '拒单',
  35 + 'field' => '预定描述',
  36 + 'sid' => '商家编号',
  37 + 'pnum' => '用餐人数',
  38 + 'rdate' => '预定开始时间',
  39 + 'edate' => '预定结束时间',
  40 + 'ddate' => '预定到店时间',
  41 + 'channel' => '预定来源渠道',
  42 + 'type' => '预定类型',
  43 + 'sex' => '用户性别',
  44 + 'phone' => '用户电话',
  45 + 'code' => '编码CODE',
  46 + 'cusdem' => '用餐描述',
  47 + 'ddate_solt' => '用餐时段',
  48 + 'room_demand'=> '包间ID',
  49 + 'openid' => '用户openid',
  50 + ),
4 'edited' => '编辑了', 51 'edited' => '编辑了',
5 'deled' => '删除了', 52 'deled' => '删除了',
6 'added' => '添加了', 53 'added' => '添加了',
7 - 'App.Customer.DelCustomerInfo' => '删除了联系人',  
8 /*********** 日志记录 **********/ 54 /*********** 日志记录 **********/
9 'hello' => '欢迎使用', 55 'hello' => '欢迎使用',
10 'adminInfo' => '个人信息', 56 'adminInfo' => '个人信息',
  1 +{include file="common/head"/}
  2 +<div class="admin-main layui-anim layui-anim-upbit">
  3 + <title>纳极科技 - 变更详情</title>
  4 + <div class="header">
  5 + <h1>订单详情</h1>
  6 + <h3>操作人:{$html.nickname} {$html.action} | 修改时间:{$html.cdate}</h3>
  7 + </div>
  8 + <div class="table-responsive">
  9 + <table class="layui-table" lay-even lay-skin="line">
  10 + <colgroup>
  11 + <col width="40%">
  12 + <col>
  13 + </colgroup>
  14 + <thead>
  15 + <tr>
  16 + <th class="text-center" colspan="2">{:lang('systemInfo')}</th>
  17 + </tr>
  18 + </thead>
  19 + <tbody>
  20 + <tr>
  21 + <td>商家编号</td>
  22 + <td>{$html.sid}</td>
  23 + </tr>
  24 + <tr>
  25 + <td>预定编号</td>
  26 + <td>{$html.rid}</td>
  27 + </tr>
  28 + <tr>
  29 + <td>操作人昵称</td>
  30 + <td>{$html.nickname}</td>
  31 + </tr>
  32 + <tr>
  33 + <td>商家名称</td>
  34 + <td>{$html.name}</td>
  35 + </tr>
  36 + <tr>
  37 + <td>操作设备</td>
  38 + <td>{$html.device}</td>
  39 + </tr>
  40 + <tr>
  41 + <td>预定人</td>
  42 + <td>{$html.rname}{$html.sex}</td>
  43 + </tr>
  44 + <tr>
  45 + <td>电话</td>
  46 + <td>{$html.phone}</td>
  47 + </tr>
  48 + </tbody>
  49 + </table>
  50 + 订单的一生:
  51 + xxxxx..
  52 + </div>
  53 +</div>
  54 +{include file="common/foot"/}
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5 + <title>纳极科技 - 变更详情</title>
  6 +
  7 + <link rel="stylesheet" href="/static/jsondiff/reset.css" type="text/css" media="screen">
  8 + <link rel="stylesheet" href="/static/jsondiff/throbber.css" type="text/css" media="screen">
  9 + <link rel="stylesheet" href="/static/jsondiff/jdd.css" type="text/css" media="screen">
  10 +
  11 + <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js" charset="utf-8"></script>
  12 +
  13 + <script src="/static/jsondiff/jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
  14 + <script src="/static/jsondiff/jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
  15 + <script src="/static/jsondiff/jdd.js" type="text/javascript" charset="utf-8"></script>
  16 + </head>
  17 + <body>
  18 +
  19 + <div id="main">
  20 + <div class="header">
  21 + <h1>变更详情</h1>
  22 + <h3>操作人:{$html.nickname} | 修改时间:{$html.cdate}</h3>
  23 + </div>
  24 +
  25 + <div class="initContainer">
  26 + <div class="left">
  27 + <textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1">{$old}</textarea>
  28 + <pre id="errorLeft" class="error"></pre>
  29 +
  30 + </div>
  31 +
  32 + <div class="center">
  33 + <button id="compare" tabindex="3">Compare</button>
  34 + <div class="throbber-loader"></div>
  35 + <br/><br/>
  36 + or try some<br /><a href="#" id="sample">sample data</a>
  37 +
  38 + </div>
  39 +
  40 + <div class="right">
  41 + <textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2">{$new}</textarea>
  42 + <pre id="errorRight" class="error"></pre>
  43 + </div>
  44 + </div>
  45 +
  46 + <div class="diffcontainer">
  47 + <div id="report">
  48 + </div>
  49 + <pre id="out" class="left" class="codeBlock"></pre>
  50 + <pre id="out2" class="right" class="codeBlock"></pre>
  51 + <ul id="toolbar" class="toolbar"></ul>
  52 + </div>
  53 + </div>
  54 +
  55 + </body>
  56 +</html>
@@ -4,10 +4,14 @@ @@ -4,10 +4,14 @@
4 <legend>{:$title}</legend> 4 <legend>{:$title}</legend>
5 </fieldset> 5 </fieldset>
6 <div class="demoTable"> 6 <div class="demoTable">
7 - <!--商家: 7 + 商家:
8 <div class="layui-input-inline"> 8 <div class="layui-input-inline">
9 <input class="layui-input" name="key" id="key" placeholder="{:lang('pleaseEnter')}店铺名称" autocomplete="off"> 9 <input class="layui-input" name="key" id="key" placeholder="{:lang('pleaseEnter')}店铺名称" autocomplete="off">
10 </div> 10 </div>
  11 + 昵称:
  12 + <div class="layui-input-inline">
  13 + <input class="layui-input" name="key" id="nickname" placeholder="{:lang('pleaseEnter')}用户昵称或者账号" autocomplete="off">
  14 + </div>
11 日期范围: 15 日期范围:
12 <div class="layui-input-inline" > 16 <div class="layui-input-inline" >
13 <input type="text" style = "width:300px"; class="layui-input" id="time" placeholder="默认为当天的统计情况"> 17 <input type="text" style = "width:300px"; class="layui-input" id="time" placeholder="默认为当天的统计情况">
@@ -23,20 +27,18 @@ @@ -23,20 +27,18 @@
23 </select> 27 </select>
24 </div> 28 </div>
25 </div> 29 </div>
26 - 筛选条件: 30 + 身份:
27 <div class="layui-input-inline layui-form"> 31 <div class="layui-input-inline layui-form">
28 <div class="layui-input-8"> 32 <div class="layui-input-8">
29 - <select name="class" id="time_type" lay-filter="myselect">  
30 - <option value="1">录入时间</option>  
31 - <option value="2">到店时间</option> 33 + <select name="class" id="iden" lay-filter="myselect">
  34 + <option value="-1">全部</option>
  35 + {volist name="identity" id="vo"}
  36 + <option value="{$vo.key}">{$vo.val}</option>
  37 + {/volist}
32 </select> 38 </select>
33 </div> 39 </div>
34 - </div>-->  
35 - <!--地区:  
36 - <div class="layui-input-inline">  
37 - <input type="text" class="layui-input" id="area" placeholder="地区暂未开放">  
38 </div> 40 </div>
39 - <button class="layui-btn" id="search" data-type="reload">查询统计</button>--> 41 + <button class="layui-btn" id="search" data-type="reload">查询统计</button>
40 </div> 42 </div>
41 <table class="layui-table" id="list" lay-filter="list"></table> 43 <table class="layui-table" id="list" lay-filter="list"></table>
42 </div> 44 </div>
@@ -47,7 +49,7 @@ @@ -47,7 +49,7 @@
47 layui.use(['table','form','laydate'], function() { 49 layui.use(['table','form','laydate'], function() {
48 var table = layui.table,laydate = layui.laydate,$ = layui.jquery; 50 var table = layui.table,laydate = layui.laydate,$ = layui.jquery;
49 console.log(layui); 51 console.log(layui);
50 - var tableIn = table.render({ 52 + var tableIn = table.render({
51 elem: '#list', 53 elem: '#list',
52 url: '{:url("log")}', 54 url: '{:url("log")}',
53 method: 'post', 55 method: 'post',
@@ -55,8 +57,15 @@ @@ -55,8 +57,15 @@
55 page:true, 57 page:true,
56 toolbar: '#toolbarDemo', 58 toolbar: '#toolbarDemo',
57 cols: [[ 59 cols: [[
58 - {field: 'parm', align: 'center',title: '内容'},  
59 - {field: 'time',width:210, title: '时间'}, 60 + {field: 'rid', align: 'center',title: '预定ID'},
  61 + {field: 'sid', align: 'center',title: '商家ID'},
  62 + {field: 'name', align: 'center',title: '商家名字'},
  63 + {field: 'cdate',width:210, title: '变更时间'},
  64 + {field: 'nickname', align: 'center',title: '变更人'},
  65 + {field: 'device',width:210, title: '操作设备'},
  66 + {field: 'field',width:210, title: '变更字段'},
  67 + {field: 'old',width:210, title: '变更前'},
  68 + {field: 'new',width:210, title: '变更后'},
60 ]], 69 ]],
61 done: function(res, curr, count){ 70 done: function(res, curr, count){
62 //code 71 //code
@@ -66,7 +75,7 @@ @@ -66,7 +75,7 @@
66 ,limit:15 75 ,limit:15
67 }); 76 });
68 77
69 - /*//日期时间范围 78 + //日期时间范围
70 laydate.render({ 79 laydate.render({
71 elem: '#time' 80 elem: '#time'
72 ,type: 'datetime' 81 ,type: 'datetime'
@@ -77,23 +86,18 @@ @@ -77,23 +86,18 @@
77 $('#search').on('click', function() { 86 $('#search').on('click', function() {
78 //关键字 87 //关键字
79 var key = $('#key').val(); 88 var key = $('#key').val();
80 - //选中的商家ID  
81 - var checkStatus = table.checkStatus('testReload'); //test即为参数id设定的值  
82 - var a = [];  
83 - $(checkStatus.data).each(function(i,o){  
84 - a.push(o.id);  
85 - });  
86 - console.log(a); 89 + //昵称
  90 + var nickname = $('#nickname').val();
  91 + //日期范围
87 var time = $('#time').val(); 92 var time = $('#time').val();
88 - console.log(time);  
89 //商家来源 93 //商家来源
90 var channel = $('#channel').val(); 94 var channel = $('#channel').val();
91 console.log(channel); 95 console.log(channel);
92 - //筛选条件  
93 - var time_type = $('#time_type').val();  
94 - console.log(time_type); 96 + //身份
  97 + var iden = $('#iden').val();
  98 + console.log(iden);
95 //传参 99 //传参
96 - tableIn.reload({ page: {page: 1},where: {key: key,sid: a,time: time,time_type: time_type,channel: channel}}); 100 + tableIn.reload({ page: {page: 1},where: {key: key,nickname: nickname,time: time,iden: iden,channel: channel}});
97 }); 101 });
98 102
99 var $ = layui.$, active = { 103 var $ = layui.$, active = {
@@ -111,6 +115,6 @@ @@ -111,6 +115,6 @@
111 } 115 }
112 }); 116 });
113 } 117 }
114 - };*/ 118 + };
115 }) 119 })
116 </script> 120 </script>
  1 +{
  2 + "curly": true,
  3 + "bitwise": false,
  4 + "eqeqeq": true,
  5 + "es3": true,
  6 + "forin": true,
  7 + "freeze": true,
  8 + "immed": true,
  9 + "indent": 4,
  10 + "latedef": true,
  11 + "newcap": true,
  12 + "noarg": true,
  13 + "noempty": true,
  14 + "nonbsp": true,
  15 + "nonew": true,
  16 + "quotmark": true,
  17 + "undef": true,
  18 + "strict": true,
  19 + "globalstrict": true,
  20 + "browser": true,
  21 + "jquery": true,
  22 + "devel": true,
  23 + "globals": {
  24 + "iac": false
  25 + },
  26 + "predef": [ "_","JSON", "jsl", "jdd", "QUnit" ],
  27 + "maxparams": 14,
  28 + "maxdepth": 7,
  29 + "maxcomplexity": 15
  30 +}
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
  4 +
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "[]"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright [2015-2017] [Zack Grossbart]
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
  202 +
  203 +
  1 +JSON Diff
  2 +==================================================
  3 +
  4 +JSON Diff expands on the amazing work by the guys at [jsonlint.com](http://www.jsonlint.com) and provides a semantic compare tool for JSON documents.
  5 +
  6 +I often work with large documents and it's difficult to see the differences between them. Normal text compare tools work well for finding the differences in JavaScript code, but JSON data can have many differences in the text that don't actually change the data.
  7 +
  8 +JSON Diff sorts, formats, and compares two JSON documents to find the actual semantic differences instead of just the text ones.
  9 +
  10 +Try it out: [http://www.jsondiff.com](http://www.jsondiff.com)
  11 +
  12 +Run the built-in unit tests: [http://www.jsondiff.com/index-test.html](http://www.jsondiff.com/index-test.html)
No preview for this file type
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5 + <title>JSON Diff - The semantic JSON compare tool</title>
  6 +
  7 + <link rel="stylesheet" href="reset.css" type="text/css" media="screen">
  8 + <link rel="stylesheet" href="throbber.css" type="text/css" media="screen">
  9 + <link rel="stylesheet" href="qunit-2.1.1.css" type="text/css" media="screen">
  10 + <link rel="stylesheet" href="jdd.css" type="text/css" media="screen">
  11 +
  12 +
  13 + <script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  14 +
  15 + <script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
  16 + <script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
  17 + <script src="qunit-2.1.1.js" type="text/javascript" charset="utf-8"></script>
  18 + <script src="jdd.js" type="text/javascript" charset="utf-8"></script>
  19 + <script src="jdd_test.js" type="text/javascript" charset="utf-8"></script>
  20 +
  21 + <script>
  22 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  23 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  24 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  25 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  26 +
  27 + ga('create', 'UA-26336682-2', 'auto');
  28 + ga('send', 'pageview');
  29 +
  30 + </script>
  31 + </head>
  32 + <body>
  33 +
  34 + <div id="main">
  35 + <div class="header">
  36 + <h1>JSON Diff - Unit Tests</h1>
  37 + <h3>The semantic JSON compare tool</h3>
  38 +
  39 + <div class="weak">
  40 + <p>
  41 + Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
  42 + </p>
  43 +
  44 + <p>
  45 + Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
  46 + </p>
  47 +
  48 + <p>
  49 + Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
  50 + </p>
  51 +
  52 + </div>
  53 + </div>
  54 +
  55 + <div class="initContainer">
  56 + <div class="left">
  57 + <textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
  58 + <pre id="errorLeft" class="error"></pre>
  59 + <span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')"></span>
  60 + </div>
  61 +
  62 + <div class="center">
  63 + <button id="compare">Compare</button>
  64 + <div class="throbber-loader"></div>
  65 + <br/><br/><br/><br/>
  66 + or try some <a href="#" id="sample">sample data</a>
  67 +
  68 + </div>
  69 +
  70 + <div class="right">
  71 + <textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
  72 + <pre id="errorRight" class="error"></pre>
  73 + <span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')"></span>
  74 + </div>
  75 + </div>
  76 +
  77 + <div class="diffcontainer">
  78 + <div id="report">
  79 + </div>
  80 + <pre id="out" class="left" class="codeBlock"></pre>
  81 + <pre id="out2" class="right" class="codeBlock"></pre>
  82 + <ul id="toolbar" class="toolbar"></ul>
  83 + </div>
  84 +
  85 + <br />
  86 + <div id="qunit"></div>
  87 + <div id="qunit-fixture"></div>
  88 + </div>
  89 +
  90 + </body>
  91 +</html>
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5 + <title>JSON Diff - The semantic JSON compare tool</title>
  6 +
  7 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/reset.css" type="text/css" media="screen">
  8 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/throbber.css" type="text/css" media="screen">
  9 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/jdd.css" type="text/css" media="screen">
  10 +
  11 + <script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  12 +
  13 + <script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
  14 + <script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
  15 + <script src="jdd.js" type="text/javascript" charset="utf-8"></script>
  16 +
  17 + <script>
  18 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  19 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  20 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  21 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  22 +
  23 + ga('create', 'UA-26336682-2', 'auto');
  24 + ga('send', 'pageview');
  25 +
  26 + </script>
  27 + </head>
  28 + <body>
  29 +
  30 + <div id="main">
  31 + <div class="header">
  32 + <h1>变更详情</h1>
  33 + <h3>The semantic JSON compare tool</h3>
  34 + </div>
  35 +
  36 + <div class="initContainer">
  37 + <div class="left">
  38 + <textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1"></textarea>
  39 + <pre id="errorLeft" class="error"></pre>
  40 + <span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')" tabindex="4"></span>
  41 + </div>
  42 +
  43 + <div class="center">
  44 + <button id="compare" tabindex="3">Compare</button>
  45 + <div class="throbber-loader"></div>
  46 + <br/><br/>
  47 + or try some<br /><a href="#" id="sample">sample data</a>
  48 +
  49 + </div>
  50 +
  51 + <div class="right">
  52 + <textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2"></textarea>
  53 + <pre id="errorRight" class="error"></pre>
  54 + <span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')" tabindex="5"></span>
  55 + </div>
  56 + </div>
  57 +
  58 + <div class="diffcontainer">
  59 + <div id="report">
  60 + </div>
  61 + <pre id="out" class="left" class="codeBlock"></pre>
  62 + <pre id="out2" class="right" class="codeBlock"></pre>
  63 + <ul id="toolbar" class="toolbar"></ul>
  64 + </div>
  65 + </div>
  66 +
  67 + </body>
  68 +</html>
  1 +body {
  2 + padding: 0;
  3 + margin: 0;
  4 + font-family: Grotesk, "Helvetica Neue", Helvetica, sans-serif;
  5 + font-size: .9rem;
  6 +}
  7 +
  8 +h1 {
  9 + font-size: 3em;
  10 + line-height: 1;
  11 + margin-bottom: .1em;
  12 + font-weight: 300;
  13 +}
  14 +
  15 +h3 {
  16 + font-size: 1.5em;
  17 + line-height: 1;
  18 + margin-bottom: 1em;
  19 + margin-top: 1em;
  20 +}
  21 +
  22 +a {
  23 + text-decoration: none;
  24 + color: #36c;
  25 +}
  26 +
  27 +a:hover {
  28 + text-decoration: underline;
  29 +}
  30 +
  31 +#main {
  32 + margin: 25px auto;
  33 + width: 95%
  34 +}
  35 +
  36 +pre.codeBlock, div.codeBlock {
  37 + overflow:auto;
  38 + margin:0 0 1em;
  39 + padding:.5em 1em;
  40 +}
  41 +
  42 +pre.codeBlock .codeLine,
  43 +pre.codeBlock .line-number,
  44 +div.codeBlock .codeLine,
  45 +div.codeBlock .line-number {
  46 + font:normal normal 13px/16px monospace;
  47 + font-family: Monaco, monospace;
  48 + color:black;
  49 + display:block;
  50 +}
  51 +
  52 +pre.codeBlock.codeBlock span.code, div.codeBlock span.code {
  53 + min-width: 97%;
  54 + display: inline-block;
  55 +}
  56 +
  57 +pre.codeBlock .line-number, div.codeBlock .line-number {
  58 + margin:0 1em 0 -1em;
  59 + border-right:1px solid #eee;
  60 + color: #bbb;
  61 + text-align:right;
  62 + -webkit-user-select: none; /* Chrome/Safari */
  63 + -moz-user-select: none; /* Firefox */
  64 + -ms-user-select: none; /* IE10+ */
  65 +
  66 + -o-user-select: none;
  67 + user-select: none;
  68 + padding-left: 0.2em;
  69 + padding-right: 0.2em;
  70 +}
  71 +
  72 +pre.codeBlock .line-number span, div.codeBlock .line-number span {
  73 + display:block;
  74 + padding:0 .5em 0 1em;
  75 +}
  76 +
  77 +pre.codeBlock .cl {
  78 + display:block;
  79 + clear:both;
  80 +}
  81 +
  82 +div.diffcontainer {
  83 + position: relative;
  84 + margin: 0 auto;
  85 + display: none;
  86 +}
  87 +
  88 +div.left {
  89 + width: 40%;
  90 + display: inline-block;
  91 +}
  92 +
  93 +div.right {
  94 + width: 40%;
  95 + display: inline-block;
  96 + vertical-align: top;
  97 +}
  98 +
  99 +div.center {
  100 + width: 14%;
  101 + display: inline-block;
  102 + vertical-align: top;
  103 + text-align: left;
  104 + margin-top: 20%;
  105 + margin-left: 2%;
  106 + text-align: center;
  107 +}
  108 +
  109 +
  110 +pre.codeBlock.left {
  111 + width: 40%;
  112 + display: inline-block;
  113 + vertical-align: top;
  114 +}
  115 +
  116 +pre.codeBlock.right {
  117 + width: 40%;
  118 + display: inline-block;
  119 + vertical-align: top;
  120 + margin-left: 1em;
  121 +}
  122 +
  123 +ul.toolbar {
  124 + width: 11%;
  125 + display: inline-block;
  126 + right: 10px;
  127 + top: 5em;
  128 + position: absolute;
  129 +}
  130 +
  131 +#report .reportTitle {
  132 + border: thin solid lightgray;
  133 + border-radius: 4px;
  134 + margin-bottom: 1em;
  135 + padding: 5px;
  136 + background-color: rgba(50, 57, 88, 0.1);
  137 + color: black;
  138 + width: 15em;
  139 +}
  140 +
  141 +
  142 +ul.toolbar li {
  143 + border: thin solid lightgray;
  144 + border-radius: 4px;
  145 + margin-bottom: 1em;
  146 + padding: 5px;
  147 + background-color: rgba(50, 57, 88, 0.1);
  148 + color: black;
  149 + cursor: pointer;
  150 + line-height: 18px;
  151 +}
  152 +
  153 +ul.toolbar li code {
  154 + margin: 0 2px;
  155 + padding: 0px 5px;
  156 + border: 1px solid #eaeaea;
  157 + background-color: #f8f8f8;
  158 + border-radius: 3px;
  159 + color: #333;
  160 + line-height: 20px;
  161 + font-family: Monaco, monospace;
  162 + font-size: 0.85em;
  163 +}
  164 +
  165 +#buttons {
  166 + white-space: nowrap;
  167 + margin-bottom: 1em;
  168 +}
  169 +
  170 +#buttons a {
  171 + color: #8a6d3b;
  172 + font-size: 18px;
  173 +}
  174 +
  175 +#buttons a.disabled {
  176 + color: #999;
  177 + cursor: auto;
  178 +}
  179 +
  180 +#buttons a.disabled:hover {
  181 + text-decoration: none;
  182 + cursor: default;
  183 +}
  184 +
  185 +#prevNextLabel {
  186 + width: 80%;
  187 + text-align: center;
  188 + display: inline-block;
  189 +}
  190 +
  191 +div.gutter {
  192 + float: left;
  193 +}
  194 +
  195 +div.codeLine span.diff {
  196 + cursor: pointer;
  197 + color: #c00;
  198 +}
  199 +
  200 +div.codeLine span.eq_off, div.codeLine span.type_off, div.codeLine span.missing_off {
  201 + cursor: auto;
  202 +}
  203 +
  204 +div.codeLine span.eq {
  205 + background: rgba(50, 57, 88, 0.1);
  206 + color: #333;
  207 +}
  208 +
  209 +div.codeLine span.type {
  210 + background: rgba(220, 100, 100, 0.1);
  211 +}
  212 +
  213 +div.codeLine span.missing {
  214 + background: rgba(0, 160, 80, 0.1);
  215 + color: green;
  216 +}
  217 +
  218 +div.codeLine span.selected {
  219 + background: #cef;
  220 + color: #47e;
  221 +}
  222 +
  223 +canvas.center {
  224 + position: absolute;
  225 + left: 40%;
  226 + width: 20%;
  227 + height: 100%;
  228 + top: 0;
  229 +}
  230 +
  231 +textarea {
  232 + width: 100%;
  233 + min-height: 500px;
  234 + padding: 5px;
  235 + background: #f3f3f3;
  236 + border-radius: 4px;
  237 + resize: vertical;
  238 + white-space: pre;
  239 + word-wrap: normal;
  240 +}
  241 +
  242 +textarea.error {
  243 + border-color: #FBC2C4;
  244 +}
  245 +
  246 +pre.error {
  247 + background: #FBE3E4;
  248 + color: #D12F19;
  249 + border: thin solid #FBC2C4;
  250 + border-radius: 4px;
  251 + padding: 1em;
  252 + display: none;
  253 +}
  254 +
  255 +span.filterBlock {
  256 + margin-bottom: 1em;
  257 +}
  258 +
  259 +span.filterBlock input {
  260 + margin-left: 2em;
  261 +}
  262 +
  263 +#report {
  264 + margin-bottom: 1em;
  265 + position: relative;
  266 + height: 4em;
  267 +}
  268 +
  269 +#report button {
  270 + display: block;
  271 + position: absolute;
  272 + top: 0;
  273 + left: 56.5%;
  274 + border-color: #36c;
  275 + color: #36c;
  276 +}
  277 +
  278 +button {
  279 + font-size: 1.2em;
  280 + color: #333;
  281 + border: 1px solid #333;
  282 + border-radius: 4px;
  283 + background: white;
  284 + padding: .5rem .7rem;
  285 + cursor: pointer;
  286 +}
  287 +
  288 +.weak {
  289 + color: #333;
  290 + max-width: 25rem;
  291 + font-size: .9em;
  292 + position: absolute;
  293 + top: 29px;
  294 + left: 56.5%;
  295 + right: 3%;
  296 + padding: 0 1em 0 0;
  297 +}
  298 +
  299 +p {
  300 + margin-bottom: 1em;
  301 +}
  302 +
  303 +.header {
  304 + height: 8em;
  305 +}
  306 +
  307 +span.fileInput {
  308 + margin-left: 2em;
  309 + margin-top: 0.5em;
  310 + display: inline-block;
  311 +}
  312 +
  313 +.throbber-loader:not(:required) {
  314 + display: none;
  315 +}
  316 +
  317 +.progress .throbber-loader:not(:required) {
  318 + display: block;
  319 + margin: 5px auto;
  320 +}
  1 +/*******************************************************************************
  2 + *
  3 + * Copyright 2015-2019 Zack Grossbart
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + *
  17 + ******************************************************************************/
  18 +'use strict';
  19 +
  20 +// utilites
  21 +//
  22 +/**
  23 + * Fixing typeof
  24 + * takes value and returns type of value
  25 + * @param value
  26 + * return typeof value
  27 + */
  28 +function getType(value) {
  29 + if ((function () { return value && (value !== this); }).call(value)) {
  30 + //fallback on 'typeof' for truthy primitive values
  31 + return typeof value;
  32 + }
  33 + return ({}).toString.call(value).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  34 +}
  35 +/**
  36 + * Iterate over array of objects and call given callback for each item in the array
  37 + * Optionally may take this as scope
  38 + *
  39 + * @param array
  40 + * @param callback
  41 + * @param optional scope
  42 + */
  43 +function forEach(array, callback, scope) {
  44 + for (var idx = 0; idx < array.length; idx++) {
  45 + callback.call(scope, array[idx], idx, array);
  46 + }
  47 +}
  48 +
  49 +/**
  50 + * The jdd object handles all of the functions for the main page. It finds the diffs and manages
  51 + * the interactions of displaying them.
  52 + */
  53 +/*global jdd:true */
  54 +var jdd = {
  55 +
  56 + LEFT: 'left',
  57 + RIGHT: 'right',
  58 +
  59 + EQUALITY: 'eq',
  60 + TYPE: 'type',
  61 + MISSING: 'missing',
  62 + diffs: [],
  63 + requestCount: 0,
  64 +
  65 + /**
  66 + * Find the differences between the two objects and recurse into their sub objects.
  67 + */
  68 + findDiffs: function (/*Object*/ config1, /*Object*/ data1, /*Object*/ config2, /*Object*/ data2) {
  69 + config1.currentPath.push('/');
  70 + config2.currentPath.push('/');
  71 +
  72 + var key;
  73 + // no un-used vars
  74 + // var val;
  75 +
  76 + if (data1.length < data2.length) {
  77 + /*
  78 + * This means the second data has more properties than the first.
  79 + * We need to find the extra ones and create diffs for them.
  80 + */
  81 + for (key in data2) {
  82 + if (data2.hasOwnProperty(key)) {
  83 + // no un-used vars
  84 + // val = data1[key];
  85 + if (!data1.hasOwnProperty(key)) {
  86 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  87 + config2, jdd.generatePath(config2, '/' + key),
  88 + 'The right side of this object has more items than the left side', jdd.MISSING));
  89 + }
  90 + }
  91 + }
  92 + }
  93 +
  94 + /*
  95 + * Now we're going to look for all the properties in object one and
  96 + * compare them to object two
  97 + */
  98 + for (key in data1) {
  99 + if (data1.hasOwnProperty(key)) {
  100 + // no un-used vars
  101 + // val = data1[key];
  102 +
  103 + config1.currentPath.push(key);
  104 +
  105 + if (!data2.hasOwnProperty(key)) {
  106 + /*
  107 + * This means that the first data has a property which
  108 + * isn't present in the second data
  109 + */
  110 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  111 + config2, jdd.generatePath(config2),
  112 + 'Missing property <code>' + key + '</code> from the object on the right side', jdd.MISSING));
  113 + } else {
  114 + config2.currentPath.push(key);
  115 +
  116 + jdd.diffVal(data1[key], config1, data2[key], config2);
  117 + config2.currentPath.pop();
  118 + }
  119 + config1.currentPath.pop();
  120 + }
  121 + }
  122 +
  123 + config1.currentPath.pop();
  124 + config2.currentPath.pop();
  125 +
  126 + /*
  127 + * Now we want to look at all the properties in object two that
  128 + * weren't in object one and generate diffs for them.
  129 + */
  130 + for (key in data2) {
  131 + if (data2.hasOwnProperty(key)) {
  132 + // no un-used vars
  133 + // val = data1[key];
  134 +
  135 + if (!data1.hasOwnProperty(key)) {
  136 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  137 + config2, jdd.generatePath(config2, key),
  138 + 'Missing property <code>' + key + '</code> from the object on the left side', jdd.MISSING));
  139 + }
  140 + }
  141 + }
  142 + },
  143 +
  144 + /**
  145 + * Generate the differences between two values. This handles differences of object
  146 + * types and actual values.
  147 + */
  148 + diffVal: function (val1, config1, val2, config2) {
  149 +
  150 + if (getType(val1) === 'array') {
  151 + jdd.diffArray(val1, config1, val2, config2);
  152 + } else if (getType(val1) === 'object') {
  153 + if (['array', 'string', 'number', 'boolean', 'null'].indexOf(getType(val2)) > -1) {
  154 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  155 + config2, jdd.generatePath(config2),
  156 + '两边对象不一致', jdd.TYPE));
  157 + } else {
  158 + jdd.findDiffs(config1, val1, config2, val2);
  159 + }
  160 + } else if (getType(val1) === 'string') {
  161 + if (getType(val2) !== 'string') {
  162 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  163 + config2, jdd.generatePath(config2),
  164 + '两边字符串类型不一致', jdd.TYPE));
  165 + } else if (val1 !== val2) {
  166 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  167 + config2, jdd.generatePath(config2),
  168 + '两边的字符串不相等', jdd.EQUALITY));
  169 + }
  170 + } else if (getType(val1) === 'number') {
  171 + if (getType(val2) !== 'number') {
  172 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  173 + config2, jdd.generatePath(config2),
  174 + '两边的数字不相等', jdd.TYPE));
  175 + } else if (val1 !== val2) {
  176 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  177 + config2, jdd.generatePath(config2),
  178 + '两边的数字不相等', jdd.EQUALITY));
  179 + }
  180 + } else if (getType(val1) === 'boolean') {
  181 + jdd.diffBool(val1, config1, val2, config2);
  182 + } else if (getType(val1) === 'null' && getType(val2) !== 'null') {
  183 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  184 + config2, jdd.generatePath(config2),
  185 + '两边类型不对', jdd.TYPE));
  186 + }
  187 + },
  188 +
  189 + /**
  190 + * Arrays are more complex because we need to recurse into them and handle different length
  191 + * issues so we handle them specially in this function.
  192 + */
  193 + diffArray: function (val1, config1, val2, config2) {
  194 + if (getType(val2) !== 'array') {
  195 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  196 + config2, jdd.generatePath(config2),
  197 + 'Both types should be arrays', jdd.TYPE));
  198 + return;
  199 + }
  200 +
  201 + if (val1.length < val2.length) {
  202 + /*
  203 + * Then there were more elements on the right side and we need to
  204 + * generate those differences.
  205 + */
  206 + for (var i = val1.length; i < val2.length; i++) {
  207 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  208 + config2, jdd.generatePath(config2, '[' + i + ']'),
  209 + 'Missing element <code>' + i + '</code> from the array on the left side', jdd.MISSING));
  210 + }
  211 + }
  212 + val1.forEach(function (arrayVal, index) {
  213 + if (val2.length <= index) {
  214 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1, '[' + index + ']'),
  215 + config2, jdd.generatePath(config2),
  216 + 'Missing element <code>' + index + '</code> from the array on the right side', jdd.MISSING));
  217 + } else {
  218 + config1.currentPath.push('/[' + index + ']');
  219 + config2.currentPath.push('/[' + index + ']');
  220 +
  221 + if (getType(val2) === 'array') {
  222 + /*
  223 + * If both sides are arrays then we want to diff them.
  224 + */
  225 + jdd.diffVal(val1[index], config1, val2[index], config2);
  226 + }
  227 + config1.currentPath.pop();
  228 + config2.currentPath.pop();
  229 + }
  230 + });
  231 + },
  232 +
  233 + /**
  234 + * We handle boolean values specially because we can show a nicer message for them.
  235 + */
  236 + diffBool: function (val1, config1, val2, config2) {
  237 + if (getType(val2) !== 'boolean') {
  238 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  239 + config2, jdd.generatePath(config2),
  240 + 'Both types should be booleans', jdd.TYPE));
  241 + } else if (val1 !== val2) {
  242 + if (val1) {
  243 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  244 + config2, jdd.generatePath(config2),
  245 + 'The left side is <code>true</code> and the right side is <code>false</code>', jdd.EQUALITY));
  246 + } else {
  247 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  248 + config2, jdd.generatePath(config2),
  249 + 'The left side is <code>false</code> and the right side is <code>true</code>', jdd.EQUALITY));
  250 + }
  251 + }
  252 + },
  253 +
  254 + /**
  255 + * Format the object into the output stream and decorate the data tree with
  256 + * the data about this object.
  257 + */
  258 + formatAndDecorate: function (/*Object*/ config, /*Object*/ data) {
  259 + if (getType(data) === 'array') {
  260 + jdd.formatAndDecorateArray(config, data);
  261 + return;
  262 + }
  263 +
  264 + jdd.startObject(config);
  265 + config.currentPath.push('/');
  266 +
  267 + var props = jdd.getSortedProperties(data);
  268 +
  269 + /*
  270 + * If the first set has more than the second then we will catch it
  271 + * when we compare values. However, if the second has more then
  272 + * we need to catch that here.
  273 + */
  274 + props.forEach(function (key) {
  275 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '"' + jdd.unescapeString(key) + '": ';
  276 + config.currentPath.push(key);
  277 + config.paths.push({
  278 + path: jdd.generatePath(config),
  279 + line: config.line
  280 + });
  281 + jdd.formatVal(data[key], config);
  282 + config.currentPath.pop();
  283 + });
  284 +
  285 + jdd.finishObject(config);
  286 + config.currentPath.pop();
  287 + },
  288 +
  289 + /**
  290 + * Format the array into the output stream and decorate the data tree with
  291 + * the data about this object.
  292 + */
  293 + formatAndDecorateArray: function (/*Object*/ config, /*Array*/ data) {
  294 + jdd.startArray(config);
  295 +
  296 + /*
  297 + * If the first set has more than the second then we will catch it
  298 + * when we compare values. However, if the second has more then
  299 + * we need to catch that here.
  300 + */
  301 + data.forEach(function (arrayVal, index) {
  302 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
  303 + config.paths.push({
  304 + path: jdd.generatePath(config, '[' + index + ']'),
  305 + line: config.line
  306 + });
  307 +
  308 + config.currentPath.push('/[' + index + ']');
  309 + jdd.formatVal(arrayVal, config);
  310 + config.currentPath.pop();
  311 + });
  312 +
  313 + jdd.finishArray(config);
  314 + config.currentPath.pop();
  315 + },
  316 +
  317 + /**
  318 + * Generate the start of the an array in the output stream and push in the new path
  319 + */
  320 + startArray: function (config) {
  321 + config.indent++;
  322 + config.out += '[';
  323 +
  324 + if (config.paths.length === 0) {
  325 + /*
  326 + * Then we are at the top of the array and we want to add
  327 + * a path for it.
  328 + */
  329 + config.paths.push({
  330 + path: jdd.generatePath(config),
  331 + line: config.line
  332 + });
  333 + }
  334 +
  335 + if (config.indent === 0) {
  336 + config.indent++;
  337 + }
  338 + },
  339 +
  340 + /**
  341 + * Finish the array, outdent, and pop off all the path
  342 + */
  343 + finishArray: function (config) {
  344 + if (config.indent === 0) {
  345 + config.indent--;
  346 + }
  347 +
  348 + jdd.removeTrailingComma(config);
  349 +
  350 + config.indent--;
  351 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']';
  352 + if (config.indent !== 0) {
  353 + config.out += ',';
  354 + } else {
  355 + config.out += jdd.newLine(config);
  356 + }
  357 + },
  358 +
  359 + /**
  360 + * Generate the start of the an object in the output stream and push in the new path
  361 + */
  362 + startObject: function (config) {
  363 + config.indent++;
  364 + config.out += '{';
  365 +
  366 + if (config.paths.length === 0) {
  367 + /*
  368 + * Then we are at the top of the object and we want to add
  369 + * a path for it.
  370 + */
  371 + config.paths.push({
  372 + path: jdd.generatePath(config),
  373 + line: config.line
  374 + });
  375 + }
  376 +
  377 + if (config.indent === 0) {
  378 + config.indent++;
  379 + }
  380 + },
  381 +
  382 + /**
  383 + * Finish the object, outdent, and pop off all the path
  384 + */
  385 + finishObject: function (config) {
  386 + if (config.indent === 0) {
  387 + config.indent--;
  388 + }
  389 +
  390 + jdd.removeTrailingComma(config);
  391 +
  392 + config.indent--;
  393 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '}';
  394 + if (config.indent !== 0) {
  395 + config.out += ',';
  396 + } else {
  397 + config.out += jdd.newLine(config);
  398 + }
  399 + },
  400 +
  401 + /**
  402 + * Format a specific value into the output stream.
  403 + */
  404 + formatVal: function (val, config) {
  405 + if (getType(val) === 'array') {
  406 + config.out += '[';
  407 +
  408 + config.indent++;
  409 + val.forEach(function (arrayVal, index) {
  410 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
  411 + config.paths.push({
  412 + path: jdd.generatePath(config, '[' + index + ']'),
  413 + line: config.line
  414 + });
  415 +
  416 + config.currentPath.push('/[' + index + ']');
  417 + jdd.formatVal(arrayVal, config);
  418 + config.currentPath.pop();
  419 + });
  420 + jdd.removeTrailingComma(config);
  421 + config.indent--;
  422 +
  423 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']' + ',';
  424 + } else if (getType(val) === 'object') {
  425 + jdd.formatAndDecorate(config, val);
  426 + } else if (getType(val) === 'string') {
  427 + config.out += '"' + jdd.unescapeString(val) + '",';
  428 + } else if (getType(val) === 'number') {
  429 + config.out += val + ',';
  430 + } else if (getType(val) === 'boolean') {
  431 + config.out += val + ',';
  432 + } else if (getType(val) === 'null') {
  433 + config.out += 'null,';
  434 + }
  435 + },
  436 +
  437 + /**
  438 + * When we parse the JSON string we end up removing the escape strings when we parse it
  439 + * into objects. This results in invalid JSON if we insert those strings back into the
  440 + * generated JSON. We also need to look out for characters that change the line count
  441 + * like new lines and carriage returns.
  442 + *
  443 + * This function puts those escaped values back when we generate the JSON output for the
  444 + * well known escape strings in JSON. It handles properties and values.
  445 + *
  446 + * This function does not handle unicode escapes. Unicode escapes are optional in JSON
  447 + * and the JSON output is still valid with a unicode character in it.
  448 + */
  449 + unescapeString: function (val) {
  450 + if (val) {
  451 + return val.replace('\\', '\\\\') // Single slashes need to be replaced first
  452 + .replace(/\"/g, '\\"') // Then double quotes
  453 + .replace(/\n/g, '\\n') // New lines
  454 + .replace('\b', '\\b') // Backspace
  455 + .replace(/\f/g, '\\f') // Formfeed
  456 + .replace(/\r/g, '\\r') // Carriage return
  457 + .replace(/\t/g, '\\t'); // Horizontal tabs
  458 + } else {
  459 + return val;
  460 + }
  461 + },
  462 +
  463 + /**
  464 + * Generate a JSON path based on the specific configuration and an optional property.
  465 + */
  466 + generatePath: function (config, prop) {
  467 + var s = '';
  468 + config.currentPath.forEach(function (path) {
  469 + s += path;
  470 + });
  471 +
  472 + if (prop) {
  473 + s += '/' + prop;
  474 + }
  475 +
  476 + if (s.length === 0) {
  477 + return '/';
  478 + } else {
  479 + return s;
  480 + }
  481 + },
  482 +
  483 + /**
  484 + * Add a new line to the output stream
  485 + */
  486 + newLine: function (config) {
  487 + config.line++;
  488 + return '\n';
  489 + },
  490 +
  491 + /**
  492 + * Sort all the relevant properties and return them in an alphabetical sort by property key
  493 + */
  494 + getSortedProperties: function (/*Object*/ obj) {
  495 + var props = [];
  496 +
  497 + for (var prop in obj) {
  498 + if (obj.hasOwnProperty(prop)) {
  499 + props.push(prop);
  500 + }
  501 + }
  502 +
  503 + props = props.sort(function (a, b) {
  504 + return a.localeCompare(b);
  505 + });
  506 +
  507 + return props;
  508 + },
  509 +
  510 + /**
  511 + * Generate the diff and verify that it matches a JSON path
  512 + */
  513 + generateDiff: function (config1, path1, config2, path2, /*String*/ msg, type) {
  514 + if (path1 !== '/' && path1.charAt(path1.length - 1) === '/') {
  515 + path1 = path1.substring(0, path1.length - 1);
  516 + }
  517 +
  518 + if (path2 !== '/' && path2.charAt(path2.length - 1) === '/') {
  519 + path2 = path2.substring(0, path2.length - 1);
  520 + }
  521 + var pathObj1 = config1.paths.find(function (path) {
  522 + return path.path === path1;
  523 + });
  524 + var pathObj2 = config2.paths.find(function (path) {
  525 + return path.path === path2;
  526 + });
  527 +
  528 + if (!pathObj1) {
  529 + throw 'Unable to find line number for (' + msg + '): ' + path1;
  530 + }
  531 +
  532 + if (!pathObj2) {
  533 + throw 'Unable to find line number for (' + msg + '): ' + path2;
  534 + }
  535 +
  536 + return {
  537 + path1: pathObj1,
  538 + path2: pathObj2,
  539 + type: type,
  540 + msg: msg
  541 + };
  542 + },
  543 +
  544 + /**
  545 + * Get the current indent level
  546 + */
  547 + getTabs: function (/*int*/ indent) {
  548 + var s = '';
  549 + for (var i = 0; i < indent; i++) {
  550 + s += ' ';
  551 + }
  552 +
  553 + return s;
  554 + },
  555 +
  556 + /**
  557 + * Remove the trailing comma from the output.
  558 + */
  559 + removeTrailingComma: function (config) {
  560 + /*
  561 + * Remove the trailing comma
  562 + */
  563 + if (config.out.charAt(config.out.length - 1) === ',') {
  564 + config.out = config.out.substring(0, config.out.length - 1);
  565 + }
  566 + },
  567 +
  568 + /**
  569 + * Create a config object for holding differences
  570 + */
  571 + createConfig: function () {
  572 + return {
  573 + out: '',
  574 + indent: -1,
  575 + currentPath: [],
  576 + paths: [],
  577 + line: 1
  578 + };
  579 + },
  580 +
  581 + /**
  582 + * Format the output pre tags.
  583 + */
  584 + formatPRETags: function () {
  585 + forEach($('pre'), function (pre) {
  586 + var codeBlock = $('<pre class="codeBlock"></pre>');
  587 + var lineNumbers = $('<div class="gutter"></div>');
  588 + codeBlock.append(lineNumbers);
  589 +
  590 + var codeLines = $('<div></div>');
  591 + codeBlock.append(codeLines);
  592 +
  593 + var addLine = function (line, index) {
  594 + var div = $('<div class="codeLine line' + (index + 1) + '"></div>');
  595 + lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
  596 +
  597 + var span = $('<span class="code"></span');
  598 + span.text(line);
  599 + div.append(span);
  600 +
  601 + codeLines.append(div);
  602 + };
  603 +
  604 + var lines = $(pre).text().split('\n');
  605 + lines.forEach(addLine);
  606 +
  607 + codeBlock.addClass($(pre).attr('class'));
  608 + codeBlock.attr('id', $(pre).attr('id'));
  609 +
  610 + $(pre).replaceWith(codeBlock);
  611 + });
  612 + },
  613 +
  614 + /**
  615 + * Format the text edits which handle the JSON input
  616 + */
  617 + formatTextAreas: function () {
  618 + forEach($('textarea'), function (textarea) {
  619 + var codeBlock = $('<div class="codeBlock"></div>');
  620 + var lineNumbers = $('<div class="gutter"></div>');
  621 + codeBlock.append(lineNumbers);
  622 +
  623 + var addLine = function (line, index) {
  624 + lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
  625 + };
  626 +
  627 + var lines = $(textarea).val().split('\n');
  628 + lines.forEach(addLine);
  629 +
  630 + $(textarea).replaceWith(codeBlock);
  631 + codeBlock.append(textarea);
  632 + });
  633 + },
  634 +
  635 + handleDiffClick: function (line, side) {
  636 + var diffs = jdd.diffs.filter(function (diff) {
  637 + if (side === jdd.LEFT) {
  638 + return line === diff.path1.line;
  639 + } else if (side === jdd.RIGHT) {
  640 + return line === diff.path2.line;
  641 + } else {
  642 + return line === diff.path1.line || line === diff.path2.line;
  643 + }
  644 + });
  645 +
  646 + $('pre.left span.code').removeClass('selected');
  647 + $('pre.right span.code').removeClass('selected');
  648 + $('ul.toolbar').text('');
  649 + diffs.forEach(function (diff) {
  650 + $('pre.left div.line' + diff.path1.line + ' span.code').addClass('selected');
  651 + $('pre.right div.line' + diff.path2.line + ' span.code').addClass('selected');
  652 + });
  653 +
  654 + if (side === jdd.LEFT || side === jdd.RIGHT) {
  655 + jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
  656 + return diff.path1.line === line;
  657 + });
  658 + }
  659 +
  660 + if (jdd.currentDiff === -1) {
  661 + jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
  662 + return diff.path2.line === line;
  663 + });
  664 + }
  665 +
  666 + var buttons = $('<div id="buttons"><div>');
  667 + var prev = $('<a href="#" title="Previous difference" id="prevButton">&lt;</a>');
  668 + prev.addClass('disabled');
  669 + prev.click(function (e) {
  670 + e.preventDefault();
  671 + jdd.highlightPrevDiff();
  672 + });
  673 + buttons.append(prev);
  674 +
  675 + buttons.append('<span id="prevNextLabel"></span>');
  676 +
  677 + var next = $('<a href="#" title="Next difference" id="nextButton">&gt;</a>');
  678 + next.click(function (e) {
  679 + e.preventDefault();
  680 + jdd.highlightNextDiff();
  681 + });
  682 + buttons.append(next);
  683 +
  684 + $('ul.toolbar').append(buttons);
  685 + jdd.updateButtonStyles();
  686 +
  687 + jdd.showDiffDetails(diffs);
  688 + },
  689 +
  690 + highlightPrevDiff: function () {
  691 + if (jdd.currentDiff > 0) {
  692 + jdd.currentDiff--;
  693 + jdd.highlightDiff(jdd.currentDiff);
  694 + jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
  695 +
  696 + jdd.updateButtonStyles();
  697 + }
  698 + },
  699 +
  700 + highlightNextDiff: function () {
  701 + if (jdd.currentDiff < jdd.diffs.length - 1) {
  702 + jdd.currentDiff++;
  703 + jdd.highlightDiff(jdd.currentDiff);
  704 + jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
  705 +
  706 + jdd.updateButtonStyles();
  707 + }
  708 + },
  709 +
  710 + updateButtonStyles: function () {
  711 + $('#prevButton').removeClass('disabled');
  712 + $('#nextButton').removeClass('disabled');
  713 +
  714 + $('#prevNextLabel').text((jdd.currentDiff + 1) + ' of ' + (jdd.diffs.length));
  715 +
  716 + if (jdd.currentDiff === 1) {
  717 + $('#prevButton').addClass('disabled');
  718 + } else if (jdd.currentDiff === jdd.diffs.length - 1) {
  719 + $('#nextButton').addClass('disabled');
  720 + }
  721 + },
  722 +
  723 + /**
  724 + * Highlight the diff at the specified index
  725 + */
  726 + highlightDiff: function (index) {
  727 + jdd.handleDiffClick(jdd.diffs[index].path1.line, jdd.BOTH);
  728 + },
  729 +
  730 + /**
  731 + * Show the details of the specified diff
  732 + */
  733 + showDiffDetails: function (diffs) {
  734 + diffs.forEach(function (diff) {
  735 + var li = $('<li></li>');
  736 + li.html(diff.msg);
  737 + $('ul.toolbar').append(li);
  738 +
  739 + li.click(function () {
  740 + jdd.scrollToDiff(diff);
  741 + });
  742 +
  743 + });
  744 + },
  745 +
  746 + /**
  747 + * Scroll the specified diff to be visible
  748 + */
  749 + scrollToDiff: function (diff) {
  750 + $('html, body').animate({
  751 + scrollTop: $('pre.left div.line' + diff.path1.line + ' span.code').offset().top
  752 + }, 0);
  753 + },
  754 +
  755 + /**
  756 + * Process the specified diff
  757 + */
  758 + processDiffs: function () {
  759 + var left = [];
  760 + var right = [];
  761 + jdd.diffs.forEach(function (diff) {
  762 + $('pre.left div.line' + diff.path1.line + ' span.code').addClass(diff.type).addClass('diff');
  763 + if (left.indexOf(diff.path1.line) === -1) {
  764 + $('pre.left div.line' + diff.path1.line + ' span.code').click(function () {
  765 + jdd.handleDiffClick(diff.path1.line, jdd.LEFT);
  766 + });
  767 + left.push(diff.path1.line);
  768 + }
  769 +
  770 + $('pre.right div.line' + diff.path2.line + ' span.code').addClass(diff.type).addClass('diff');
  771 + if (right.indexOf(diff.path2.line) === -1) {
  772 + $('pre.right div.line' + diff.path2.line + ' span.code').click(function () {
  773 + jdd.handleDiffClick(diff.path2.line, jdd.RIGHT);
  774 + });
  775 + right.push(diff.path2.line);
  776 + }
  777 + });
  778 +
  779 + jdd.diffs = jdd.diffs.sort(function (a, b) {
  780 + return a.path1.line - b.path1.line;
  781 + });
  782 +
  783 + },
  784 +
  785 + /**
  786 + * Validate the input against the JSON parser
  787 + */
  788 + validateInput: function (json, side) {
  789 + try {
  790 + jsl.parser.parse(json);
  791 +
  792 + if (side === jdd.LEFT) {
  793 + $('#errorLeft').text('').hide();
  794 + $('#textarealeft').removeClass('error');
  795 + } else {
  796 + $('#errorRight').text('').hide();
  797 + $('#textarearight').removeClass('error');
  798 + }
  799 +
  800 + return true;
  801 + } catch (parseException) {
  802 + if (side === jdd.LEFT) {
  803 + $('#errorLeft').text(parseException.message).show();
  804 + $('#textarealeft').addClass('error');
  805 + } else {
  806 + $('#errorRight').text(parseException.message).show();
  807 + $('#textarearight').addClass('error');
  808 + }
  809 + return false;
  810 + }
  811 + },
  812 +
  813 + /**
  814 + * Handle the file uploads
  815 + */
  816 + handleFiles: function (files, side) {
  817 + var reader = new FileReader();
  818 +
  819 + reader.onload = (function () {
  820 + return function (e) {
  821 + if (side === jdd.LEFT) {
  822 + $('#textarealeft').val(e.target.result);
  823 + } else {
  824 + $('#textarearight').val(e.target.result);
  825 + }
  826 + };
  827 + })(files[0]);
  828 +
  829 + reader.readAsText(files[0]);
  830 + },
  831 +
  832 + setupNewDiff: function () {
  833 + $('div.initContainer').show();
  834 + $('div.diffcontainer').hide();
  835 + $('div.diffcontainer pre').text('');
  836 + $('ul.toolbar').text('');
  837 + },
  838 +
  839 + /**
  840 + * Generate the report section with the diff
  841 + */
  842 + generateReport: function () {
  843 + var report = $('#report');
  844 +
  845 + report.text('');
  846 +
  847 + var newDiff = $('');
  848 + report.append(newDiff);
  849 + newDiff.click(function () {
  850 + jdd.setupNewDiff();
  851 + });
  852 +
  853 + if (jdd.diffs.length === 0) {
  854 + report.append('<span>The two files were semantically identical.</span>');
  855 + return;
  856 + }
  857 +
  858 + var typeCount = 0;
  859 + var eqCount = 0;
  860 + var missingCount = 0;
  861 + jdd.diffs.forEach(function (diff) {
  862 + if (diff.type === jdd.EQUALITY) {
  863 + eqCount++;
  864 + } else if (diff.type === jdd.MISSING) {
  865 + missingCount++;
  866 + } else if (diff.type === jdd.TYPE) {
  867 + typeCount++;
  868 + }
  869 + });
  870 +
  871 + var title = $('<div class="reportTitle"></div>');
  872 + if (jdd.diffs.length === 1) {
  873 + title.text('找到 ' + (jdd.diffs.length) + ' 处差别');
  874 + } else {
  875 + title.text('找到 ' + (jdd.diffs.length) + ' 处差别');
  876 + }
  877 +
  878 + report.prepend(title);
  879 +
  880 + var filterBlock = $('<span class="filterBlock">显示:</span>');
  881 +
  882 + /*
  883 + * The missing checkbox
  884 + */
  885 + if (missingCount > 0) {
  886 + var missing = $('<label><input id="showMissing" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  887 + if (missingCount === 1) {
  888 + missing.append(missingCount + ' missing property');
  889 + } else {
  890 + missing.append(missingCount + ' missing properties');
  891 + }
  892 + missing.children('input').click(function () {
  893 + if (!$(this).prop('checked')) {
  894 + $('span.code.diff.missing').addClass('missing_off').removeClass('missing');
  895 + } else {
  896 + $('span.code.diff.missing_off').addClass('missing').removeClass('missing_off');
  897 + }
  898 + });
  899 + filterBlock.append(missing);
  900 + }
  901 +
  902 + /*
  903 + * The types checkbox
  904 + */
  905 + if (typeCount > 0) {
  906 + var types = $('<label><input id="showTypes" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  907 + if (typeCount === 1) {
  908 + types.append(typeCount + ' 处类型不在正确');
  909 + } else {
  910 + types.append(typeCount + ' 处类型不在正确');
  911 + }
  912 +
  913 + types.children('input').click(function () {
  914 + if (!$(this).prop('checked')) {
  915 + $('span.code.diff.type').addClass('type_off').removeClass('type');
  916 + } else {
  917 + $('span.code.diff.type_off').addClass('type').removeClass('type_off');
  918 + }
  919 + });
  920 + filterBlock.append(types);
  921 + }
  922 +
  923 + /*
  924 + * The equals checkbox
  925 + */
  926 + if (eqCount > 0) {
  927 + var eq = $('<label><input id="showEq" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  928 + if (eqCount === 1) {
  929 + eq.append(eqCount + ' 处值不一样');
  930 + } else {
  931 + eq.append(eqCount + ' 处值不一样');
  932 + }
  933 + eq.children('input').click(function () {
  934 + if (!$(this).prop('checked')) {
  935 + $('span.code.diff.eq').addClass('eq_off').removeClass('eq');
  936 + } else {
  937 + $('span.code.diff.eq_off').addClass('eq').removeClass('eq_off');
  938 + }
  939 + });
  940 + filterBlock.append(eq);
  941 + }
  942 +
  943 + report.append(filterBlock);
  944 +
  945 +
  946 + },
  947 +
  948 + /**
  949 + * Implement the compare button and complete the compare process
  950 + */
  951 + compare: function () {
  952 +
  953 + if (jdd.requestCount !== 0) {
  954 + /*
  955 + * This means we have a pending request and we just need to wait for that to finish.
  956 + */
  957 + return;
  958 + }
  959 +
  960 + $('body').addClass('progress');
  961 + $('#compare').prop('disabled', true);
  962 +
  963 + var loadUrl = function (id, errId) {
  964 + if ($('#' + id).val().trim().substring(0, 4).toLowerCase() === 'http') {
  965 + jdd.requestCount++;
  966 + $.post('proxy.php',
  967 + {
  968 + 'url': $('#' + id).val().trim()
  969 + }, function (responseObj) {
  970 + if (responseObj.error) {
  971 + $('#' + errId).text(responseObj.result).show();
  972 + $('#' + id).addClass('error');
  973 + $('body').removeClass('progress');
  974 + $('#compare').prop('disabled', false);
  975 + } else {
  976 + $('#' + id).val(responseObj.content);
  977 + jdd.requestCount--;
  978 + jdd.compare();
  979 + }
  980 + }, 'json');
  981 + return true;
  982 + } else {
  983 + return false;
  984 + }
  985 + };
  986 +
  987 + if (loadUrl('textarealeft', 'errorLeft')) {
  988 + return;
  989 + }
  990 +
  991 + if (loadUrl('textarearight', 'errorRight')) {
  992 + return;
  993 + }
  994 +
  995 + /*
  996 + * We'll start by running the text through JSONlint since it gives
  997 + * much better error messages.
  998 + */
  999 + var leftValid = jdd.validateInput($('#textarealeft').val(), jdd.LEFT);
  1000 + var rightValid = jdd.validateInput($('#textarearight').val(), jdd.RIGHT);
  1001 +
  1002 + if (!leftValid || !rightValid) {
  1003 + $('body').removeClass('progress');
  1004 + $('#compare').prop('disabled', false);
  1005 + return;
  1006 + }
  1007 +
  1008 + $('div.initContainer').hide();
  1009 + $('div.diffcontainer').show();
  1010 +
  1011 + jdd.diffs = [];
  1012 +
  1013 + var left = JSON.parse($('#textarealeft').val());
  1014 + var right = JSON.parse($('#textarearight').val());
  1015 +
  1016 +
  1017 + var config = jdd.createConfig();
  1018 + jdd.formatAndDecorate(config, left);
  1019 + $('#out').text(config.out);
  1020 +
  1021 + var config2 = jdd.createConfig();
  1022 + jdd.formatAndDecorate(config2, right);
  1023 + $('#out2').text(config2.out);
  1024 +
  1025 + jdd.formatPRETags();
  1026 +
  1027 + config.currentPath = [];
  1028 + config2.currentPath = [];
  1029 +
  1030 + jdd.diffVal(left, config, right, config2);
  1031 + jdd.processDiffs();
  1032 + jdd.generateReport();
  1033 +
  1034 + //console.log('diffs: ' + JSON.stringify(jdd.diffs));
  1035 +
  1036 + if (jdd.diffs.length > 0) {
  1037 + jdd.highlightDiff(0);
  1038 + jdd.currentDiff = 0;
  1039 + jdd.updateButtonStyles();
  1040 + }
  1041 +
  1042 + $('body').removeClass('progress');
  1043 + $('#compare').prop('disabled', false);
  1044 +
  1045 + /*
  1046 + * We want to switch the toolbar bar between fixed and absolute position when you
  1047 + * scroll so you can get the maximum number of toolbar items.
  1048 + */
  1049 + var toolbarTop = $('#toolbar').offset().top - 15;
  1050 + $(window).scroll(function () {
  1051 + if (toolbarTop < $(window).scrollTop()) {
  1052 + $('#toolbar').css('position', 'fixed').css('top', '10px');
  1053 + } else {
  1054 + $('#toolbar').css('position', 'absolute').css('top', '');
  1055 + }
  1056 + });
  1057 +
  1058 + },
  1059 +
  1060 + /**
  1061 + * Load in the sample data
  1062 + */
  1063 + loadSampleData: function () {
  1064 + $('#textarealeft').val('{"Aidan Gillen": {"array": ["Game of Thron\\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}');
  1065 + /*$('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  1066 + $('#textarearight').val('{"foo":[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');*/
  1067 + $('#textarearight').val('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}');
  1068 + },
  1069 +
  1070 + getParameterByName: function (name) {
  1071 + name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
  1072 + var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
  1073 + results = regex.exec(location.search);
  1074 + return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
  1075 + }
  1076 +};
  1077 +
  1078 +
  1079 +jQuery(document).ready(function () {
  1080 + jdd.compare();
  1081 + /*$('#compare').click(function () {
  1082 + jdd.compare();
  1083 + });*/
  1084 +
  1085 + if (jdd.getParameterByName('left')) {
  1086 + $('#textarealeft').val(jdd.getParameterByName('left'));
  1087 + }
  1088 +
  1089 + if (jdd.getParameterByName('right')) {
  1090 + $('#textarearight').val(jdd.getParameterByName('right'));
  1091 + }
  1092 +
  1093 + if (jdd.getParameterByName('left') && jdd.getParameterByName('right')) {
  1094 + jdd.compare();
  1095 + }
  1096 +
  1097 +
  1098 + $('#sample').click(function (e) {
  1099 + e.preventDefault();
  1100 + jdd.loadSampleData();
  1101 + });
  1102 +
  1103 + $(document).keydown(function (event) {
  1104 + if (event.keyCode === 78 || event.keyCode === 39) {
  1105 + /*
  1106 + * The N key or right arrow key
  1107 + */
  1108 + jdd.highlightNextDiff();
  1109 + } else if (event.keyCode === 80 || event.keyCode === 37) {
  1110 + /*
  1111 + * The P key or left arrow key
  1112 + */
  1113 + jdd.highlightPrevDiff();
  1114 + }
  1115 + });
  1116 +});
  1117 +
  1118 +// polyfills
  1119 +
  1120 +// Array.prototype.find
  1121 +// https://tc39.github.io/ecma262/#sec-array.prototype.find
  1122 +if (!Array.prototype.find) {
  1123 + Object.defineProperty(Array.prototype, 'find', {
  1124 + value: function (predicate) {
  1125 + // 1. Let O be ? ToObject(this value).
  1126 + if (this === null) {
  1127 + throw new TypeError('"this" is null or not defined');
  1128 + }
  1129 +
  1130 + var o = Object(this);
  1131 +
  1132 + // 2. Let len be ? ToLength(? Get(O, "length")).
  1133 + var len = o.length >>> 0;
  1134 +
  1135 + // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  1136 + if (typeof predicate !== 'function') {
  1137 + throw new TypeError('predicate must be a function');
  1138 + }
  1139 +
  1140 + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  1141 + var thisArg = arguments[1];
  1142 +
  1143 + // 5. Let k be 0.
  1144 + var k = 0;
  1145 +
  1146 + // 6. Repeat, while k < len
  1147 + while (k < len) {
  1148 + // a. Let Pk be ! ToString(k).
  1149 + // b. Let kValue be ? Get(O, Pk).
  1150 + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
  1151 + // d. If testResult is true, return kValue.
  1152 + var kValue = o[k];
  1153 + if (predicate.call(thisArg, kValue, k, o)) {
  1154 + return kValue;
  1155 + }
  1156 + // e. Increase k by 1.
  1157 + k++;
  1158 + }
  1159 +
  1160 + // 7. Return undefined.
  1161 + return undefined;
  1162 + },
  1163 + configurable: true,
  1164 + writable: true
  1165 + });
  1166 +}
  1167 +
  1168 +// Array.prototype.findIndex
  1169 +// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
  1170 +if (!Array.prototype.findIndex) {
  1171 + Object.defineProperty(Array.prototype, 'findIndex', {
  1172 + value: function (predicate) {
  1173 + // 1. Let O be ? ToObject(this value).
  1174 + if (this === null) {
  1175 + throw new TypeError('"this" is null or not defined');
  1176 + }
  1177 +
  1178 + var o = Object(this);
  1179 +
  1180 + // 2. Let len be ? ToLength(? Get(O, "length")).
  1181 + var len = o.length >>> 0;
  1182 +
  1183 + // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  1184 + if (typeof predicate !== 'function') {
  1185 + throw new TypeError('predicate must be a function');
  1186 + }
  1187 +
  1188 + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  1189 + var thisArg = arguments[1];
  1190 +
  1191 + // 5. Let k be 0.
  1192 + var k = 0;
  1193 +
  1194 + // 6. Repeat, while k < len
  1195 + while (k < len) {
  1196 + // a. Let Pk be ! ToString(k).
  1197 + // b. Let kValue be ? Get(O, Pk).
  1198 + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
  1199 + // d. If testResult is true, return k.
  1200 + var kValue = o[k];
  1201 + if (predicate.call(thisArg, kValue, k, o)) {
  1202 + return k;
  1203 + }
  1204 + // e. Increase k by 1.
  1205 + k++;
  1206 + }
  1207 +
  1208 + // 7. Return -1.
  1209 + return -1;
  1210 + },
  1211 + configurable: true,
  1212 + writable: true
  1213 + });
  1214 +}
  1 +/*******************************************************************************
  2 + *
  3 + * Copyright 2015-2017 Zack Grossbart
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + *
  17 + ******************************************************************************/
  18 +'use strict';
  19 +
  20 +/**
  21 + * This is the main test harness for JSONDiff. It runs unit tests and integrations tests in the browser.
  22 + * To run these tests just access the index-test.html page. You can run the tests on the live site at
  23 + * http://www.jsondiff.com/index-test.html.
  24 + *
  25 + * Each test follows the same basic pattern: set the text of the left and right text areas then run the
  26 + * jdd.compare function. Once the compare completes we examine the JSON data in the jdd.diffs object to
  27 + * determine if the test completed successfully. As a last step we clear out the two text areas and call
  28 + * jdd.setupNewDiff to prepare for the next test.
  29 + */
  30 +
  31 +/**
  32 + * This is the basic compate test. It does a compare of our sample data and makes sure that we have the
  33 + * correct number of diffs of each type. It also checks a few random diffs to make sure they are correct.
  34 + */
  35 +QUnit.test('Object compare tests', function (assert) {
  36 + $('#textarealeft').val('{"Aidan Gillen": {"array": ["Game of Thron\\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true, "null": null, "a_null": null, "another_null": "null check", "object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}');
  37 + $('#textarearight').val('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false, "null": null, "a_null":88, "another_null": null, "object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}');
  38 +
  39 + jdd.compare();
  40 +
  41 + // This test makes sure there wasn't a parsing error
  42 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  43 +
  44 + assert.ok(jdd.diffs.length === 20, 'Checking for the correct number of differences');
  45 +
  46 + var typeCount = 0;
  47 + var eqCount = 0;
  48 + var missingCount = 0;
  49 +
  50 + jdd.diffs.forEach(function (diff) {
  51 + if (diff.type === jdd.EQUALITY) {
  52 + eqCount++;
  53 + } else if (diff.type === jdd.MISSING) {
  54 + missingCount++;
  55 + } else if (diff.type === jdd.TYPE) {
  56 + typeCount++;
  57 + }
  58 + });
  59 +
  60 + assert.ok(eqCount === 4, 'Checking unequal values');
  61 + assert.ok(missingCount === 11, 'Checking missing values');
  62 + assert.ok(typeCount === 5, 'Checking incorrect types');
  63 +
  64 + $('#textarealeft').val('');
  65 + $('#textarearight').val('');
  66 + jdd.setupNewDiff();
  67 +});
  68 +
  69 +/**
  70 + * This test focuses on finding differences between objects and arrays with similar data at the
  71 + * root of the JSON document.
  72 + */
  73 +QUnit.test('Array to object compare tests', function (assert) {
  74 + $('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  75 + $('#textarearight').val('{"foo":[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');
  76 +
  77 + jdd.compare();
  78 +
  79 + // This test makes sure there wasn't a parsing error
  80 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  81 +
  82 + assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
  83 +
  84 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
  85 +
  86 + $('#textarealeft').val('');
  87 + $('#textarearight').val('');
  88 + jdd.setupNewDiff();
  89 +});
  90 +
  91 +/**
  92 + * This test focuses on comparing arrays and makes sure that we can properly tell the difference
  93 + * between missing and unequal values.
  94 + */
  95 +QUnit.test('Array compare tests', function (assert) {
  96 + $('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  97 + $('#textarearight').val('[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  98 +
  99 + jdd.compare();
  100 +
  101 + // This test makes sure there wasn't a parsing error
  102 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  103 +
  104 + assert.ok(jdd.diffs.length === 7, 'Checking for the correct number of differences');
  105 +
  106 + var eqCount = 0;
  107 + var missingCount = 0;
  108 +
  109 +
  110 + jdd.diffs.forEach(function (diff) {
  111 + if (diff.type === jdd.EQUALITY) {
  112 + eqCount++;
  113 + } else if (diff.type === jdd.MISSING) {
  114 + missingCount++;
  115 + }
  116 + });
  117 +
  118 + assert.ok(eqCount === 6, 'Checking unequal values');
  119 + assert.ok(missingCount === 1, 'Checking missing values');
  120 +
  121 + $('#textarealeft').val('');
  122 + $('#textarearight').val('');
  123 + jdd.setupNewDiff();
  124 +});
  125 +
  126 +/**
  127 + * This test works a lot like the array to object compare tests, but it switches the left and right
  128 + * values. We've had a few bugs that only show up when the difference is on a specific side.
  129 + */
  130 +QUnit.test('Object to array compare tests', function (assert) {
  131 + $('#textarealeft').val('{"foo":[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');
  132 + $('#textarearight').val('[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  133 +
  134 + jdd.compare();
  135 +
  136 + // This test makes sure there wasn't a parsing error
  137 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  138 +
  139 + assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
  140 +
  141 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
  142 +
  143 + $('#textarealeft').val('');
  144 + $('#textarearight').val('');
  145 + jdd.setupNewDiff();
  146 +});
  147 +
  148 +/**
  149 + * These tests cover differences with whitespace in them. Specifically cases where there are new lines, backspaces,
  150 + * tabs, and other whitespace characters within the JSON keys and values. These values are escaped in the JSON source,
  151 + * but show up unescaped in the JSON object. We need to make sure we add them back and convert the line numbers properly.
  152 + */
  153 +QUnit.test('Whitespace formatting tests', function (assert) {
  154 + $('#textarealeft').val('{"newline": "a\\nb","slash": "a\\\\b","quotes": "a\\"b","backspace": "a\\bb","formfeed": "a\\fb","carriagereturn": "a\\rb","tab": "a\\tb","a\\nb": "newline","a\\\\b": "slash","a\\"b": "quotes","a\\bb": "backspace","a\\fb": "formfeed","a\\rb": "carriagereturn","a\\tb": "tab"}');
  155 + $('#textarearight').val('{"newline": "a\\nbx","slash": "a\\\\bx","quotes": "a\\"bx","backspace": "a\\bbx","formfeed": "a\\fbx","carriagereturn": "a\\rbx","tab": "a\\tbx","a\\nb": "newline","a\\\\bx": "slash","a\\"bx": "quotes","a\\bbx": "backspace","a\\fbx": "formfeed","a\\rbx": "carriagereturn","a\\tbx": "tab"}');
  156 +
  157 + jdd.compare();
  158 +
  159 + // This test makes sure there wasn't a parsing error
  160 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  161 +
  162 + assert.ok(jdd.diffs.length === 19, 'Checking for the correct number of differences');
  163 +
  164 + assert.ok(jdd.diffs[0].type === jdd.MISSING, 'Checking missing property');
  165 + assert.ok(jdd.diffs[0].msg === 'Missing property <code>a\\bx</code> from the object on the left side', 'Checking property formatting');
  166 +
  167 + assert.ok(jdd.diffs[9].type === jdd.MISSING, 'Checking missing property');
  168 + assert.ok(jdd.diffs[9].msg === 'Missing property <code>a\"b</code> from the object on the right side', 'Checking property formatting');
  169 +
  170 + assert.ok(jdd.diffs[14].type === jdd.EQUALITY, 'Checking missing property');
  171 +
  172 + assert.ok(jdd.diffs[18].type === jdd.EQUALITY, 'Checking missing property');
  173 +
  174 + $('#textarealeft').val('');
  175 + $('#textarearight').val('');
  176 + jdd.setupNewDiff();
  177 +});
  178 +
  179 +/**
  180 + * This test tests for null values when compared to arrays on the right side of the compare.
  181 + */
  182 +QUnit.test('Null array length tests - right side', function (assert) {
  183 + $('#textarealeft').val('{ "akey": [] }');
  184 + $('#textarearight').val('{ "akey": null }');
  185 +
  186 + jdd.compare();
  187 +
  188 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  189 +
  190 + $('#textarealeft').val('');
  191 + $('#textarearight').val('');
  192 + jdd.setupNewDiff();
  193 +});
  194 +
  195 +/**
  196 + * This test tests for null values when compared to arrays on the left side of the compare.
  197 + */
  198 +QUnit.test('Null array length tests - left side', function (assert) {
  199 + $('#textarealeft').val('{ "akey": null }');
  200 + $('#textarearight').val('{ "akey": [] }');
  201 +
  202 + jdd.compare();
  203 +
  204 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  205 +
  206 + $('#textarealeft').val('');
  207 + $('#textarearight').val('');
  208 + jdd.setupNewDiff();
  209 +});
  210 +
  211 +
  212 +/**
  213 + * This test tests for null values when compared to arrays on the right side of the compare.
  214 + */
  215 +QUnit.test('Null object length tests - right side', function (assert) {
  216 + $('#textarealeft').val('{ "akey": {} }');
  217 + $('#textarearight').val('{ "akey": null }');
  218 +
  219 + jdd.compare();
  220 +
  221 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  222 +
  223 + $('#textarealeft').val('');
  224 + $('#textarearight').val('');
  225 + jdd.setupNewDiff();
  226 +});
  227 +
  228 +/**
  229 + * This test tests for null values when compared to arrays on the left side of the compare.
  230 + */
  231 +QUnit.test('Null object length tests - left side', function (assert) {
  232 + $('#textarealeft').val('{ "akey": null }');
  233 + $('#textarearight').val('{ "akey": {} }');
  234 +
  235 + jdd.compare();
  236 +
  237 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  238 +
  239 + $('#textarealeft').val('');
  240 + $('#textarearight').val('');
  241 + jdd.setupNewDiff();
  242 +});
  243 +
  244 +QUnit.test('Escaped quotes test', function (assert) {
  245 + $('#textarealeft').val('{"link": "<a href=\\\"http://google.com/\\\">Google</a>"}');
  246 + $('#textarearight').val('{"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"}');
  247 +
  248 + jdd.compare();
  249 + assert.ok(jdd.diffs[0].type === jdd.EQUALITY, 'Checking correct type');
  250 + assert.ok($('pre.right div.line2 span').text().trim() === '"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"', 'Checking escaped quotes');
  251 + assert.ok($('pre.left div.line2 span').text().trim() === '"link": "<a href=\\\"http://google.com/\\\">Google</a>"', 'Checking escaped quotes');
  252 +
  253 + $('#textarealeft').val('');
  254 + $('#textarearight').val('');
  255 + jdd.setupNewDiff();
  256 +});
  257 +
  258 +
  259 +QUnit.done(function () {
  260 + $('div.initContainer').hide();
  261 + $('div.diffContainer').hide();
  262 +});
  1 +/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
  2 +var jsl = typeof jsl === 'undefined' ? {} : jsl;
  3 +
  4 +/**
  5 + * jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability).
  6 + *
  7 +**/
  8 +jsl.format = (function () {
  9 +
  10 + function repeat(s, count) {
  11 + return new Array(count + 1).join(s);
  12 + }
  13 +
  14 + function formatJson(json) {
  15 + var i = 0,
  16 + il = 0,
  17 + tab = " ",
  18 + newJson = "",
  19 + indentLevel = 0,
  20 + inString = false,
  21 + currentChar = null;
  22 +
  23 + for (i = 0, il = json.length; i < il; i += 1) {
  24 + currentChar = json.charAt(i);
  25 +
  26 + switch (currentChar) {
  27 + case '{':
  28 + case '[':
  29 + if (!inString) {
  30 + newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
  31 + indentLevel += 1;
  32 + } else {
  33 + newJson += currentChar;
  34 + }
  35 + break;
  36 + case '}':
  37 + case ']':
  38 + if (!inString) {
  39 + indentLevel -= 1;
  40 + newJson += "\n" + repeat(tab, indentLevel) + currentChar;
  41 + } else {
  42 + newJson += currentChar;
  43 + }
  44 + break;
  45 + case ',':
  46 + if (!inString) {
  47 + newJson += ",\n" + repeat(tab, indentLevel);
  48 + } else {
  49 + newJson += currentChar;
  50 + }
  51 + break;
  52 + case ':':
  53 + if (!inString) {
  54 + newJson += ": ";
  55 + } else {
  56 + newJson += currentChar;
  57 + }
  58 + break;
  59 + case ' ':
  60 + case "\n":
  61 + case "\t":
  62 + if (inString) {
  63 + newJson += currentChar;
  64 + }
  65 + break;
  66 + case '"':
  67 + if (i > 0 && json.charAt(i - 1) !== '\\') {
  68 + inString = !inString;
  69 + }
  70 + newJson += currentChar;
  71 + break;
  72 + default:
  73 + newJson += currentChar;
  74 + break;
  75 + }
  76 + }
  77 +
  78 + return newJson;
  79 + }
  80 +
  81 + return { "formatJson": formatJson };
  82 +
  83 +}());
  1 +/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
  2 +var jsl = typeof jsl === 'undefined' ? {} : jsl;
  3 +
  4 +/**
  5 + * Helper Function for Caret positioning
  6 + * Gratefully borrowed from the Masked Input Plugin by Josh Bush
  7 + * http://digitalbush.com/projects/masked-input-plugin
  8 +**/
  9 +$.fn.caret = function (begin, end) {
  10 + if (this.length === 0) {
  11 + return;
  12 + }
  13 + if (typeof begin === 'number') {
  14 + end = (typeof end === 'number') ? end : begin;
  15 + return this.each(function () {
  16 + if (this.setSelectionRange) {
  17 + this.focus();
  18 + this.setSelectionRange(begin, end);
  19 + } else if (this.createTextRange) {
  20 + var range = this.createTextRange();
  21 + range.collapse(true);
  22 + range.moveEnd('character', end);
  23 + range.moveStart('character', begin);
  24 + range.select();
  25 + }
  26 + });
  27 + } else {
  28 + if (this[0].setSelectionRange) {
  29 + begin = this[0].selectionStart;
  30 + end = this[0].selectionEnd;
  31 + } else if (document.selection && document.selection.createRange) {
  32 + var range = document.selection.createRange();
  33 + begin = -range.duplicate().moveStart('character', -100000);
  34 + end = begin + range.text.length;
  35 + }
  36 + return {"begin": begin, "end": end};
  37 + }
  38 +};
  39 +
  40 +
  41 +/**
  42 + * jsl.interactions - provides support for interactions within JSON Lint.
  43 + *
  44 +**/
  45 +jsl.interactions = (function () {
  46 + var reformatParam,
  47 + reformat,
  48 + compress;
  49 +
  50 +
  51 + /******* UTILITY METHODS *******/
  52 +
  53 + /**
  54 + * Get the Nth position of a character in a string
  55 + * @searchStr the string to search through
  56 + * @char the character to find
  57 + * @pos int the nth character to find, 1 based.
  58 + *
  59 + * @return int the position of the character found
  60 + **/
  61 + function getNthPos(searchStr, char, pos) {
  62 + var i,
  63 + charCount = 0,
  64 + strArr = searchStr.split(char);
  65 +
  66 + if (pos === 0) {
  67 + return 0;
  68 + }
  69 +
  70 + for (i = 0; i < pos; i++) {
  71 + if (i >= strArr.length) {
  72 + return -1;
  73 + }
  74 +
  75 + // +1 because we split out some characters
  76 + charCount += strArr[i].length + char.length;
  77 + }
  78 +
  79 + return charCount;
  80 + }
  81 +
  82 + /**
  83 + * Get a URL parameter from the current windows URL.
  84 + * Courtesy Paul Oppenheim: http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
  85 + * @param name the parameter to retrieve
  86 + * @return string the url parameter's value, if any
  87 + **/
  88 + function getURLParameter(name) {
  89 + param = (new RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || ['', null])[1];
  90 + if (param) {
  91 + return decodeURIComponent(param);
  92 + } else {
  93 + return null;
  94 + }
  95 + }
  96 +
  97 + /******* INTERACTION METHODS *******/
  98 +
  99 + /**
  100 + * Validate the JSON we've been given, displaying an error or success message.
  101 + * @return void
  102 + **/
  103 + function validate() {
  104 + var lineNum,
  105 + lineMatches,
  106 + lineStart,
  107 + lineEnd,
  108 + jsonVal,
  109 + result;
  110 +
  111 + jsonVal = $('#json_input').val();
  112 +
  113 + try {
  114 + result = jsl.parser.parse(jsonVal);
  115 +
  116 + if (result) {
  117 + $('#results').removeClass('error').addClass('success');
  118 + $('div.linedwrap').removeClass('redBorder').addClass('greenBorder');
  119 + $('#results').text('Valid JSON');
  120 +
  121 + if (reformat) {
  122 + $('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, " "));
  123 + }
  124 +
  125 + if (compress) {
  126 + $('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, ""));
  127 + }
  128 + } else {
  129 + alert("An unknown error occurred. Please contact Arc90.");
  130 + }
  131 + } catch (parseException) {
  132 +
  133 + /**
  134 + * If we failed to validate, run our manual formatter and then re-validate so that we
  135 + * can get a better line number. On a successful validate, we don't want to run our
  136 + * manual formatter because the automatic one is faster and probably more reliable.
  137 + **/
  138 + try {
  139 + if (reformat) {
  140 + jsonVal = jsl.format.formatJson($('#json_input').val());
  141 + $('#json_input').val(jsonVal);
  142 + result = jsl.parser.parse($('#json_input').val());
  143 + }
  144 + } catch(e) {
  145 + parseException = e;
  146 + }
  147 +
  148 + lineMatches = parseException.message.match(/line ([0-9]*)/);
  149 + if (lineMatches && typeof lineMatches === "object" && lineMatches.length > 1) {
  150 + lineNum = parseInt(lineMatches[1], 10);
  151 +
  152 + if (lineNum === 1) {
  153 + lineStart = 0;
  154 + } else {
  155 + lineStart = getNthPos(jsonVal, "\n", lineNum - 1);
  156 + }
  157 +
  158 + lineEnd = jsonVal.indexOf("\n", lineStart);
  159 + if (lineEnd < 0) {
  160 + lineEnd = jsonVal.length;
  161 + }
  162 +
  163 + $('#json_input').focus().caret(lineStart, lineEnd);
  164 + }
  165 +
  166 + $('#results').text(parseException.message);
  167 +
  168 + $('#results').removeClass('success').addClass('error');
  169 + $('div.linedwrap').removeClass('greenBorder').addClass('redBorder');
  170 + }
  171 +
  172 + $('#loadSpinner').hide();
  173 + }
  174 +
  175 + /**
  176 + * Initialize variables, add event listeners, etc.
  177 + *
  178 + * @return void
  179 + **/
  180 + function init() {
  181 + reformatParam = getURLParameter('reformat');
  182 + reformat = reformatParam !== '0' && reformatParam !== 'no';
  183 + compress = reformatParam === 'compress',
  184 + jsonParam = getURLParameter('json');
  185 +
  186 + if (compress) {
  187 + $('#headerText').html('JSONLint<span class="light">Compressor</span>');
  188 + }
  189 +
  190 + if (!reformat) {
  191 + $('#headerText').html('JSONLint<span class="light">Lite</span>');
  192 + }
  193 +
  194 + $('#validate').click(function () {
  195 + $('#results_header, #loadSpinner').show();
  196 +
  197 + var jsonVal = $.trim($('#json_input').val());
  198 +
  199 + if (jsonVal.substring(0, 4).toLowerCase() === "http") {
  200 + $.post("proxy.php", {"url": jsonVal}, function (responseObj) {
  201 + $('#json_input').val(responseObj.content);
  202 + validate();
  203 + }, 'json');
  204 + } else {
  205 + validate();
  206 + }
  207 +
  208 + return false;
  209 + });
  210 +
  211 + $('#json_input').keyup(function () {
  212 + $('div.linedwrap').removeClass('greenBorder').removeClass('redBorder');
  213 + }).linedtextarea({
  214 + selectedClass: 'lineselect'
  215 + }).focus();
  216 +
  217 + $('#reset').click(function () {
  218 + $('#json_input').val('').focus();
  219 + });
  220 +
  221 + $('#faqButton').click(function () {
  222 + $('#faq').slideToggle();
  223 + });
  224 +
  225 + if (jsonParam) {
  226 + $('#json_input').val(jsonParam);
  227 + $('#validate').click();
  228 + }
  229 + }
  230 +
  231 + return {
  232 + 'init': init
  233 + };
  234 +}());
  235 +
  236 +$(function () {
  237 + jsl.interactions.init();
  238 +});
  1 +var jsl = typeof(jsl) === 'undefined' ? {} : jsl;
  2 +/**
  3 + * JSON Lint Parser gratefully provided by Zach Carter
  4 + * https://github.com/zaach/jsonlint
  5 +**/
  6 +jsl.parser = function(){var a=!0,b=!1,c={},d=function(){var a={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONObject:13,EOF:14,JSONArray:15,JSONValue:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[12,2],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[13,2],[13,3],[20,3],[19,1],[19,3],[15,2],[15,3],[25,1],[25,3]],performAction:function(a,b,c,d,e,f,g){var h=f.length-1;switch(e){case 1:this.$=a;break;case 2:this.$=Number(a);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=f[h-1];case 7:return this.$=f[h-1];case 8:this.$=f[h];break;case 9:this.$=f[h];break;case 10:this.$=f[h];break;case 11:this.$=f[h];break;case 12:this.$=f[h];break;case 13:this.$=f[h];break;case 14:this.$={};break;case 15:this.$=f[h-1];break;case 16:this.$=[f[h-2],f[h]];break;case 17:this.$={},this.$[f[h][0]]=f[h][1];break;case 18:this.$=f[h-2],f[h-2][f[h][0]]=f[h][1];break;case 19:this.$=[];break;case 20:this.$=f[h-1];break;case 21:this.$=[f[h]];break;case 22:this.$=f[h-2],f[h-2].push(f[h])}},table:[{12:1,13:2,15:3,17:[1,4],23:[1,5]},{1:[3]},{14:[1,6]},{14:[1,7]},{3:11,4:[1,12],18:[1,8],19:9,20:10},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:15,17:[1,4],23:[1,5],24:[1,13],25:14},{1:[2,6]},{1:[2,7]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{18:[1,26],22:[1,27]},{18:[2,17],22:[2,17]},{21:[1,28]},{18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{22:[1,30],24:[1,29]},{22:[2,21],24:[2,21]},{18:[2,8],22:[2,8],24:[2,8]},{18:[2,9],22:[2,9],24:[2,9]},{18:[2,10],22:[2,10],24:[2,10]},{18:[2,11],22:[2,11],24:[2,11]},{18:[2,12],22:[2,12],24:[2,12]},{18:[2,13],22:[2,13],24:[2,13]},{18:[2,3],22:[2,3],24:[2,3]},{18:[2,4],22:[2,4],24:[2,4]},{18:[2,5],22:[2,5],24:[2,5]},{18:[2,2],22:[2,2],24:[2,2]},{14:[2,15],18:[2,15],22:[2,15],24:[2,15]},{3:11,4:[1,12],20:31},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:32,17:[1,4],23:[1,5]},{14:[2,20],18:[2,20],22:[2,20],24:[2,20]},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:33,17:[1,4],23:[1,5]},{18:[2,18],22:[2,18]},{18:[2,16],22:[2,16]},{22:[2,22],24:[2,22]}],defaultActions:{6:[2,6],7:[2,7]},parseError:function(a,b){throw new Error(a)},parse:function(a){function o(){var a;a=b.lexer.lex()||1,typeof a!="number"&&(a=b.symbols_[a]||a);return a}function n(a){c.length=c.length-2*a,d.length=d.length-a,e.length=e.length-a}var b=this,c=[0],d=[null],e=[],f=this.table,g="",h=0,i=0,j=0,k=2,l=1;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var m=this.lexer.yylloc;e.push(m),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var p,q,r,s,t,u,v={},w,x,y,z;for(;;){r=c[c.length-1],this.defaultActions[r]?s=this.defaultActions[r]:(p==null&&(p=o()),s=f[r]&&f[r][p]);if(typeof s=="undefined"||!s.length||!s[0]){if(!j){z=[];for(w in f[r])this.terminals_[w]&&w>2&&z.push("'"+this.terminals_[w]+"'");var A="";this.lexer.showPosition?A="Parse error on line "+(h+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+z.join(", "):A="Parse error on line "+(h+1)+": Unexpected "+(p==1?"end of input":"'"+(this.terminals_[p]||p)+"'"),this.parseError(A,{text:this.lexer.match,token:this.terminals_[p]||p,line:this.lexer.yylineno,loc:m,expected:z})}if(j==3){if(p==l)throw new Error(A||"Parsing halted.");i=this.lexer.yyleng,g=this.lexer.yytext,h=this.lexer.yylineno,m=this.lexer.yylloc,p=o()}for(;;){if(k.toString()in f[r])break;if(r==0)throw new Error(A||"Parsing halted.");n(1),r=c[c.length-1]}q=p,p=k,r=c[c.length-1],s=f[r]&&f[r][k],j=3}if(s[0]instanceof Array&&s.length>1)throw new Error("Parse Error: multiple actions possible at state: "+r+", token: "+p);switch(s[0]){case 1:c.push(p),d.push(this.lexer.yytext),e.push(this.lexer.yylloc),c.push(s[1]),p=null,q?(p=q,q=null):(i=this.lexer.yyleng,g=this.lexer.yytext,h=this.lexer.yylineno,m=this.lexer.yylloc,j>0&&j--);break;case 2:x=this.productions_[s[1]][1],v.$=d[d.length-x],v._$={first_line:e[e.length-(x||1)].first_line,last_line:e[e.length-1].last_line,first_column:e[e.length-(x||1)].first_column,last_column:e[e.length-1].last_column},u=this.performAction.call(v,g,i,h,this.yy,s[1],d,e);if(typeof u!="undefined")return u;x&&(c=c.slice(0,-1*x*2),d=d.slice(0,-1*x),e=e.slice(0,-1*x)),c.push(this.productions_[s[1]][0]),d.push(v.$),e.push(v._$),y=f[c[c.length-2]][c[c.length-1]],c.push(y);break;case 3:return!0}}return!0}},f=function(){var a={EOF:1,parseError:function(a,b){if(this.yy.parseError)this.yy.parseError(a,b);else throw new Error(a)},setInput:function(a){this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0};return this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.match+=a,this.matched+=a;var b=a.match(/\n/);b&&this.yylineno++,this._input=this._input.slice(1);return a},unput:function(a){this._input=a+this._input;return this},more:function(){this._more=!0;return this},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;a.length<20&&(a+=this._input.substr(0,20-a.length));return(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d;this._more||(this.yytext="",this.match="");var e=this._currentRules();for(var f=0;f<e.length;f++){b=this._input.match(this.rules[e[f]]);if(b){d=b[0].match(/\n.*/g),d&&(this.yylineno+=d.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:d?d[d.length-1].length-1:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,e[f],this.conditionStack[this.conditionStack.length-1]);if(a)return a;return}}if(this._input==="")return this.EOF;this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return typeof a!="undefined"?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules}};a.performAction=function(a,b,c,d){var e=d;switch(c){case 0:break;case 1:return 6;case 2:b.yytext=b.yytext.substr(1,b.yyleng-2);return 4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},a.rules=[/^\s+/,/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][-+]?[0-9]+)?\b/,/^"(\\["bfnrt/\\]|\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f"\\])*"/,/^\{/,/^\}/,/^\[/,/^\]/,/^,/,/^:/,/^true\b/,/^false\b/,/^null\b/,/^$/,/^./],a.conditions={INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}};return a}();a.lexer=f;return a}();typeof a!="undefined"&&typeof c!="undefined"&&(c.parser=d,c.parse=function(){return d.parse.apply(d,arguments)},c.main=function(b){if(!b[1])throw new Error("Usage: "+b[0]+" FILE");if(typeof process!="undefined")var d=a("fs").readFileSync(a("path").join(process.cwd(),b[1]),"utf8");else var e=a("file").path(a("file").cwd()),d=e.join(b[1]).read({charset:"utf-8"});return c.parser.parse(d)},typeof b!="undefined"&&a.main===b&&c.main(typeof process!="undefined"?process.argv.slice(1):a("system").args));return c}()
  1 +{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"]}
  1 +<?php
  2 +
  3 +$url = filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
  4 +
  5 +if (!$url || !preg_match("/^https?:/i", $url)) {
  6 + echo '{ "result": "Invalid URL. Please check your URL and try again.", "error": true }';
  7 + return;
  8 +}
  9 +
  10 +$parsed_url = parse_url($url);
  11 +$scheme = strtolower($parsed_url['scheme']);
  12 +if (($scheme !== "http" && $scheme !== "https")) {
  13 + echo '{ "result": "Invalid URL - Please check your URL and try again.", "error": true }';
  14 + return;
  15 +}
  16 +
  17 +$ch = curl_init($url);
  18 +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  19 +curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
  20 +curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  21 +$data = curl_exec($ch);
  22 +$info = curl_getinfo($ch);
  23 +curl_close($ch);
  24 +
  25 +if ($data === false) {
  26 + echo '{ "result": "Unable to parse URL. Please check your URL and try again.", "error": true }';
  27 + return;
  28 +}
  29 +
  30 +$contentLength = intval($info['download_content_length']);
  31 +$status = intval($info['http_code']);
  32 +
  33 +if ($status >= 400) {
  34 + echo '{ "result": "URL returned bad status code ' . $status . '.", "error": true }';
  35 + return;
  36 +}
  37 +
  38 +if ($contentLength >= 52428800) {
  39 + echo '{ "result": "URL content length greater than 10 megs (' . $contentLength . '). Validation not available for files this large.", "responseCode": "1" }';
  40 + return;
  41 +}
  42 +
  43 +$response = new StdClass();
  44 +$response->status = $status;
  45 +$response->length = $contentLength;
  46 +$response->url = $info['url'];
  47 +$response->content = $data;
  48 +
  49 +echo json_encode($response);
  50 +
  51 +?>
  1 +/*!
  2 + * QUnit 2.1.1
  3 + * https://qunitjs.com/
  4 + *
  5 + * Copyright jQuery Foundation and other contributors
  6 + * Released under the MIT license
  7 + * https://jquery.org/license
  8 + *
  9 + * Date: 2017-01-06T01:52Z
  10 + */
  11 +
  12 +/** Font Family and Sizes */
  13 +
  14 +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
  15 + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
  16 +}
  17 +
  18 +#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
  19 +#qunit-tests { font-size: smaller; }
  20 +
  21 +
  22 +/** Resets */
  23 +
  24 +#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
  25 + margin: 0;
  26 + padding: 0;
  27 +}
  28 +
  29 +
  30 +/** Header (excluding toolbar) */
  31 +
  32 +#qunit-header {
  33 + padding: 0.5em 0 0.5em 1em;
  34 +
  35 + color: #8699A4;
  36 + background-color: #0D3349;
  37 +
  38 + font-size: 1.5em;
  39 + line-height: 1em;
  40 + font-weight: 400;
  41 +
  42 + border-radius: 5px 5px 0 0;
  43 +}
  44 +
  45 +#qunit-header a {
  46 + text-decoration: none;
  47 + color: #C2CCD1;
  48 +}
  49 +
  50 +#qunit-header a:hover,
  51 +#qunit-header a:focus {
  52 + color: #FFF;
  53 +}
  54 +
  55 +#qunit-banner {
  56 + height: 5px;
  57 +}
  58 +
  59 +#qunit-filteredTest {
  60 + padding: 0.5em 1em 0.5em 1em;
  61 + color: #366097;
  62 + background-color: #F4FF77;
  63 +}
  64 +
  65 +#qunit-userAgent {
  66 + padding: 0.5em 1em 0.5em 1em;
  67 + color: #FFF;
  68 + background-color: #2B81AF;
  69 + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
  70 +}
  71 +
  72 +
  73 +/** Toolbar */
  74 +
  75 +#qunit-testrunner-toolbar {
  76 + padding: 0.5em 1em 0.5em 1em;
  77 + color: #5E740B;
  78 + background-color: #EEE;
  79 +}
  80 +
  81 +#qunit-testrunner-toolbar .clearfix {
  82 + height: 0;
  83 + clear: both;
  84 +}
  85 +
  86 +#qunit-testrunner-toolbar label {
  87 + display: inline-block;
  88 +}
  89 +
  90 +#qunit-testrunner-toolbar input[type=checkbox],
  91 +#qunit-testrunner-toolbar input[type=radio] {
  92 + margin: 3px;
  93 + vertical-align: -2px;
  94 +}
  95 +
  96 +#qunit-testrunner-toolbar input[type=text] {
  97 + box-sizing: border-box;
  98 + height: 1.6em;
  99 +}
  100 +
  101 +.qunit-url-config,
  102 +.qunit-filter,
  103 +#qunit-modulefilter {
  104 + display: inline-block;
  105 + line-height: 2.1em;
  106 +}
  107 +
  108 +.qunit-filter,
  109 +#qunit-modulefilter {
  110 + float: right;
  111 + position: relative;
  112 + margin-left: 1em;
  113 +}
  114 +
  115 +.qunit-url-config label {
  116 + margin-right: 0.5em;
  117 +}
  118 +
  119 +#qunit-modulefilter-search {
  120 + box-sizing: border-box;
  121 + width: 400px;
  122 +}
  123 +
  124 +#qunit-modulefilter-search-container:after {
  125 + position: absolute;
  126 + right: 0.3em;
  127 + content: "\25bc";
  128 + color: black;
  129 +}
  130 +
  131 +#qunit-modulefilter-dropdown {
  132 + /* align with #qunit-modulefilter-search */
  133 + box-sizing: border-box;
  134 + width: 400px;
  135 + position: absolute;
  136 + right: 0;
  137 + top: 50%;
  138 + margin-top: 0.8em;
  139 +
  140 + border: 1px solid #D3D3D3;
  141 + border-top: none;
  142 + border-radius: 0 0 .25em .25em;
  143 + color: #000;
  144 + background-color: #F5F5F5;
  145 + z-index: 99;
  146 +}
  147 +
  148 +#qunit-modulefilter-dropdown a {
  149 + color: inherit;
  150 + text-decoration: none;
  151 +}
  152 +
  153 +#qunit-modulefilter-dropdown .clickable.checked {
  154 + font-weight: bold;
  155 + color: #000;
  156 + background-color: #D2E0E6;
  157 +}
  158 +
  159 +#qunit-modulefilter-dropdown .clickable:hover {
  160 + color: #FFF;
  161 + background-color: #0D3349;
  162 +}
  163 +
  164 +#qunit-modulefilter-actions {
  165 + display: block;
  166 + overflow: auto;
  167 +
  168 + /* align with #qunit-modulefilter-dropdown-list */
  169 + font: smaller/1.5em sans-serif;
  170 +}
  171 +
  172 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
  173 + box-sizing: border-box;
  174 + max-height: 2.8em;
  175 + display: block;
  176 + padding: 0.4em;
  177 +}
  178 +
  179 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
  180 + float: right;
  181 + font: inherit;
  182 +}
  183 +
  184 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
  185 + /* insert padding to align with checkbox margins */
  186 + padding-left: 3px;
  187 +}
  188 +
  189 +#qunit-modulefilter-dropdown-list {
  190 + max-height: 200px;
  191 + overflow-y: auto;
  192 + margin: 0;
  193 + border-top: 2px groove threedhighlight;
  194 + padding: 0.4em 0 0;
  195 + font: smaller/1.5em sans-serif;
  196 +}
  197 +
  198 +#qunit-modulefilter-dropdown-list li {
  199 + white-space: nowrap;
  200 + overflow: hidden;
  201 + text-overflow: ellipsis;
  202 +}
  203 +
  204 +#qunit-modulefilter-dropdown-list .clickable {
  205 + display: block;
  206 + padding-left: 0.15em;
  207 +}
  208 +
  209 +
  210 +/** Tests: Pass/Fail */
  211 +
  212 +#qunit-tests {
  213 + list-style-position: inside;
  214 +}
  215 +
  216 +#qunit-tests li {
  217 + padding: 0.4em 1em 0.4em 1em;
  218 + border-bottom: 1px solid #FFF;
  219 + list-style-position: inside;
  220 +}
  221 +
  222 +#qunit-tests > li {
  223 + display: none;
  224 +}
  225 +
  226 +#qunit-tests li.running,
  227 +#qunit-tests li.pass,
  228 +#qunit-tests li.fail,
  229 +#qunit-tests li.skipped,
  230 +#qunit-tests li.aborted {
  231 + display: list-item;
  232 +}
  233 +
  234 +#qunit-tests.hidepass {
  235 + position: relative;
  236 +}
  237 +
  238 +#qunit-tests.hidepass li.running,
  239 +#qunit-tests.hidepass li.pass {
  240 + visibility: hidden;
  241 + position: absolute;
  242 + width: 0;
  243 + height: 0;
  244 + padding: 0;
  245 + border: 0;
  246 + margin: 0;
  247 +}
  248 +
  249 +#qunit-tests li strong {
  250 + cursor: pointer;
  251 +}
  252 +
  253 +#qunit-tests li.skipped strong {
  254 + cursor: default;
  255 +}
  256 +
  257 +#qunit-tests li a {
  258 + padding: 0.5em;
  259 + color: #C2CCD1;
  260 + text-decoration: none;
  261 +}
  262 +
  263 +#qunit-tests li p a {
  264 + padding: 0.25em;
  265 + color: #6B6464;
  266 +}
  267 +#qunit-tests li a:hover,
  268 +#qunit-tests li a:focus {
  269 + color: #000;
  270 +}
  271 +
  272 +#qunit-tests li .runtime {
  273 + float: right;
  274 + font-size: smaller;
  275 +}
  276 +
  277 +.qunit-assert-list {
  278 + margin-top: 0.5em;
  279 + padding: 0.5em;
  280 +
  281 + background-color: #FFF;
  282 +
  283 + border-radius: 5px;
  284 +}
  285 +
  286 +.qunit-source {
  287 + margin: 0.6em 0 0.3em;
  288 +}
  289 +
  290 +.qunit-collapsed {
  291 + display: none;
  292 +}
  293 +
  294 +#qunit-tests table {
  295 + border-collapse: collapse;
  296 + margin-top: 0.2em;
  297 +}
  298 +
  299 +#qunit-tests th {
  300 + text-align: right;
  301 + vertical-align: top;
  302 + padding: 0 0.5em 0 0;
  303 +}
  304 +
  305 +#qunit-tests td {
  306 + vertical-align: top;
  307 +}
  308 +
  309 +#qunit-tests pre {
  310 + margin: 0;
  311 + white-space: pre-wrap;
  312 + word-wrap: break-word;
  313 +}
  314 +
  315 +#qunit-tests del {
  316 + color: #374E0C;
  317 + background-color: #E0F2BE;
  318 + text-decoration: none;
  319 +}
  320 +
  321 +#qunit-tests ins {
  322 + color: #500;
  323 + background-color: #FFCACA;
  324 + text-decoration: none;
  325 +}
  326 +
  327 +/*** Test Counts */
  328 +
  329 +#qunit-tests b.counts { color: #000; }
  330 +#qunit-tests b.passed { color: #5E740B; }
  331 +#qunit-tests b.failed { color: #710909; }
  332 +
  333 +#qunit-tests li li {
  334 + padding: 5px;
  335 + background-color: #FFF;
  336 + border-bottom: none;
  337 + list-style-position: inside;
  338 +}
  339 +
  340 +/*** Passing Styles */
  341 +
  342 +#qunit-tests li li.pass {
  343 + color: #3C510C;
  344 + background-color: #FFF;
  345 + border-left: 10px solid #C6E746;
  346 +}
  347 +
  348 +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
  349 +#qunit-tests .pass .test-name { color: #366097; }
  350 +
  351 +#qunit-tests .pass .test-actual,
  352 +#qunit-tests .pass .test-expected { color: #999; }
  353 +
  354 +#qunit-banner.qunit-pass { background-color: #C6E746; }
  355 +
  356 +/*** Failing Styles */
  357 +
  358 +#qunit-tests li li.fail {
  359 + color: #710909;
  360 + background-color: #FFF;
  361 + border-left: 10px solid #EE5757;
  362 + white-space: pre;
  363 +}
  364 +
  365 +#qunit-tests > li:last-child {
  366 + border-radius: 0 0 5px 5px;
  367 +}
  368 +
  369 +#qunit-tests .fail { color: #000; background-color: #EE5757; }
  370 +#qunit-tests .fail .test-name,
  371 +#qunit-tests .fail .module-name { color: #000; }
  372 +
  373 +#qunit-tests .fail .test-actual { color: #EE5757; }
  374 +#qunit-tests .fail .test-expected { color: #008000; }
  375 +
  376 +#qunit-banner.qunit-fail { background-color: #EE5757; }
  377 +
  378 +
  379 +/*** Aborted tests */
  380 +#qunit-tests .aborted { color: #000; background-color: orange; }
  381 +/*** Skipped tests */
  382 +
  383 +#qunit-tests .skipped {
  384 + background-color: #EBECE9;
  385 +}
  386 +
  387 +#qunit-tests .qunit-skipped-label {
  388 + background-color: #F4FF77;
  389 + display: inline-block;
  390 + font-style: normal;
  391 + color: #366097;
  392 + line-height: 1.8em;
  393 + padding: 0 0.5em;
  394 + margin: -0.4em 0.4em -0.4em 0;
  395 +}
  396 +
  397 +/** Result */
  398 +
  399 +#qunit-testresult {
  400 + color: #2B81AF;
  401 + background-color: #D2E0E6;
  402 +
  403 + border-bottom: 1px solid #FFF;
  404 +}
  405 +#qunit-testresult .clearfix {
  406 + height: 0;
  407 + clear: both;
  408 +}
  409 +#qunit-testresult .module-name {
  410 + font-weight: 700;
  411 +}
  412 +#qunit-testresult-display {
  413 + padding: 0.5em 1em 0.5em 1em;
  414 + width: 85%;
  415 + float:left;
  416 +}
  417 +#qunit-testresult-controls {
  418 + padding: 0.5em 1em 0.5em 1em;
  419 + width: 10%;
  420 + float:left;
  421 +}
  422 +
  423 +/** Fixture */
  424 +
  425 +#qunit-fixture {
  426 + position: absolute;
  427 + top: -10000px;
  428 + left: -10000px;
  429 + width: 1000px;
  430 + height: 1000px;
  431 +}
This diff could not be displayed because it is too large.
  1 +/* http://meyerweb.com/eric/tools/css/reset/
  2 + v2.0 | 20110126
  3 + License: none (public domain)
  4 +*/
  5 +
  6 +html, body, div, span, applet, object, iframe,
  7 +h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  8 +a, abbr, acronym, address, big, cite, code,
  9 +del, dfn, em, img, ins, kbd, q, s, samp,
  10 +small, strike, strong, sub, sup, tt, var,
  11 +b, u, i, center,
  12 +dl, dt, dd, ol, ul, li,
  13 +fieldset, form, label, legend,
  14 +table, caption, tbody, tfoot, thead, tr, th, td,
  15 +article, aside, canvas, details, embed,
  16 +figure, figcaption, footer, header, hgroup,
  17 +menu, nav, output, ruby, section, summary,
  18 +time, mark, audio, video {
  19 + margin: 0;
  20 + padding: 0;
  21 + border: 0;
  22 + font: inherit;
  23 + vertical-align: baseline;
  24 +}
  25 +/* HTML5 display-role reset for older browsers */
  26 +article, aside, details, figcaption, figure,
  27 +footer, header, hgroup, menu, nav, section {
  28 + display: block;
  29 +}
  30 +body {
  31 + line-height: 1;
  32 +}
  33 +ol, ul {
  34 + list-style: none;
  35 +}
  36 +blockquote, q {
  37 + quotes: none;
  38 +}
  39 +blockquote:before, blockquote:after,
  40 +q:before, q:after {
  41 + content: '';
  42 + content: none;
  43 +}
  44 +table {
  45 + border-collapse: collapse;
  46 + border-spacing: 0;
  47 +}
  1 +@-moz-keyframes throbber-loader {
  2 + 0% {
  3 + background: #dde2e7;
  4 + }
  5 + 10% {
  6 + background: #6b9dc8;
  7 + }
  8 + 40% {
  9 + background: #dde2e7;
  10 + }
  11 +}
  12 +@-webkit-keyframes throbber-loader {
  13 + 0% {
  14 + background: #dde2e7;
  15 + }
  16 + 10% {
  17 + background: #6b9dc8;
  18 + }
  19 + 40% {
  20 + background: #dde2e7;
  21 + }
  22 +}
  23 +@keyframes throbber-loader {
  24 + 0% {
  25 + background: #dde2e7;
  26 + }
  27 + 10% {
  28 + background: #6b9dc8;
  29 + }
  30 + 40% {
  31 + background: #dde2e7;
  32 + }
  33 +}
  34 +/* :not(:required) hides these rules from IE9 and below */
  35 +.throbber-loader:not(:required) {
  36 + -moz-animation: throbber-loader 2000ms 300ms infinite ease-out;
  37 + -webkit-animation: throbber-loader 2000ms 300ms infinite ease-out;
  38 + animation: throbber-loader 2000ms 300ms infinite ease-out;
  39 + background: #dde2e7;
  40 + display: inline-block;
  41 + position: relative;
  42 + text-indent: -9999px;
  43 + width: 0.9em;
  44 + height: 1.5em;
  45 + margin: 0 1.6em;
  46 +}
  47 +.throbber-loader:not(:required):before, .throbber-loader:not(:required):after {
  48 + background: #dde2e7;
  49 + content: '\x200B';
  50 + display: inline-block;
  51 + width: 0.9em;
  52 + height: 1.5em;
  53 + position: absolute;
  54 + top: 0;
  55 +}
  56 +.throbber-loader:not(:required):before {
  57 + -moz-animation: throbber-loader 2000ms 150ms infinite ease-out;
  58 + -webkit-animation: throbber-loader 2000ms 150ms infinite ease-out;
  59 + animation: throbber-loader 2000ms 150ms infinite ease-out;
  60 + left: -1.6em;
  61 +}
  62 +.throbber-loader:not(:required):after {
  63 + -moz-animation: throbber-loader 2000ms 450ms infinite ease-out;
  64 + -webkit-animation: throbber-loader 2000ms 450ms infinite ease-out;
  65 + animation: throbber-loader 2000ms 450ms infinite ease-out;
  66 + right: -1.6em;
  67 +}
  1 +{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}
  1 +<?php
  2 +
  3 +class TextDiff
  4 +{
  5 + private static $word_reg = null;
  6 +
  7 + private $old = null;
  8 + private $new = null;
  9 +
  10 + private $is_differ = false;
  11 +
  12 + private $data = null;
  13 + private $table_html = null;
  14 + private $list_html = null;
  15 +
  16 + function __construct($old, $new)
  17 + {
  18 + $this->setWordReg();
  19 + $this->old = $old;
  20 + $this->new = $new;
  21 +
  22 + $this->data = $this->getDiffData();
  23 + }
  24 +
  25 + public function getDiffArray($old, $new) {
  26 + $matrix = array();
  27 + $max = $old_max = $new_max = 0;
  28 + foreach($old as $old_key => $old_value) {
  29 + foreach(array_keys($new, $old_value) as $new_key) {
  30 + if (!empty($matrix[$old_key - 1][$new_key - 1])) {
  31 + $matrix[$old_key][$new_key] = $matrix[$old_key - 1][$new_key - 1] + 1;
  32 + } else {
  33 + $matrix[$old_key][$new_key] = 1;
  34 + }
  35 +
  36 + if ($matrix[$old_key][$new_key] > $max) {
  37 + $max = $matrix[$old_key][$new_key];
  38 + $old_max = $old_key + 1 - $max;
  39 + $new_max = $new_key + 1 - $max;
  40 + }
  41 + }
  42 + }
  43 +
  44 + if($max == 0) {
  45 + if ($old == $new) {
  46 + return $old;
  47 + } else {
  48 + return array(
  49 + array(
  50 + 'source' => $old,
  51 + 'change' => $new
  52 + )
  53 + );
  54 + }
  55 + } else {
  56 + $arr1 = $this->getDiffArray(array_slice($old, 0, $old_max), array_slice($new, 0, $new_max));
  57 + $arr2 = array_slice($new, $new_max, $max);
  58 + $arr3 = $this->getDiffArray(array_slice($old, $old_max + $max), array_slice($new, $new_max + $max));
  59 +
  60 + return array_merge($arr1, $arr2, $arr3);
  61 + }
  62 + }
  63 +
  64 + private function getDiffData()
  65 + {
  66 + if (empty($this->old) && empty($this->new)) {
  67 + return null;
  68 + }
  69 +
  70 + $line_diff = $this->getLineDiff($this->old, $this->new);
  71 +
  72 + // ダミー行を除去
  73 + $line_diff = array_filter($line_diff, function($val){return (false !== $val);});
  74 + $line_diff = array_values($line_diff);
  75 +
  76 + $lines = array();
  77 + $line_count = 1;
  78 + foreach($line_diff as $line) {
  79 + if (is_array($line) && array_key_exists('change', $line)) {
  80 + $this->is_differ = true;
  81 + $words = $this->getWordDiff($line['source'], $line['change']);
  82 + $lines[$line_count] = array(
  83 + 'line' => $line_count,
  84 + 'differ' => true,
  85 + 'source' => implode('', $line['source']),
  86 + 'change' => implode('', $line['change']),
  87 + 'words' => $words,
  88 + );
  89 +
  90 + ++$line_count;
  91 + } else {
  92 + $lines[$line_count] = array(
  93 + 'line' => $line_count,
  94 + 'differ' => false,
  95 + 'source' => $line,
  96 + 'change' => $line,
  97 + 'words' => null
  98 + );
  99 +
  100 + ++$line_count;
  101 + }
  102 + }
  103 +
  104 + return $lines;
  105 + }
  106 +
  107 + private function getLineDiff($old, $new)
  108 + {
  109 + $old = str_replace("\r\n", "\n", $old);
  110 + $new = str_replace("\r\n", "\n", $new);
  111 +
  112 + // 差異の行が続くと別の行にならない場合があるため、
  113 + // 各行の後にダミー行(false)を入れる
  114 + $old_lines = array();
  115 + foreach(explode("\n", $old) as $val) {
  116 + $old_lines[] = $val;
  117 + $old_lines[] = false;
  118 + }
  119 +
  120 + $new_lines = array();
  121 + foreach(explode("\n", $new) as $val) {
  122 + $new_lines[] = $val;
  123 + $new_lines[] = false;
  124 + }
  125 +
  126 + return $this->getDiffArray($old_lines, $new_lines);
  127 + }
  128 +
  129 + private function getWordDiff($old_array, $new_array)
  130 + {
  131 + $words = array();
  132 + $count = 0;
  133 + foreach($old_array as $key => $source) {
  134 + $change = (!empty($new_array[$key]) ? $new_array[$key] : '');
  135 + $s_source = $this->getSeparatedWord($source);
  136 + $s_change = $this->getSeparatedWord($change);
  137 + $diff = $this->getDiffArray($s_source, $s_change);
  138 +
  139 + foreach($diff as $k => $val) {
  140 + if (is_array($val) && array_key_exists('change', $val)) {
  141 + $words[] = array(
  142 + 'source' => implode('', $val['source']),
  143 + 'change' => implode('', $val['change']),
  144 + );
  145 + ++$count;
  146 + } else {
  147 + $before = $count -1;
  148 + if(@array_key_exists($before, $words) && !@array_key_exists('source', $words[$before])) {
  149 + $words[$before] .= $val;
  150 + } else {
  151 + $words[] = $val;
  152 + ++$count;
  153 + }
  154 + }
  155 + }
  156 + }
  157 +
  158 + return $words;
  159 + }
  160 +
  161 + private function setWordReg()
  162 + {
  163 + if (null === self::$word_reg) {
  164 + $regs = array(
  165 + '々〇〻\x{3220}-\x{3244}\x{3280}-\x{32B0}\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}', // 漢字
  166 + 'ぁ-ん~ー', // ひらがな
  167 + 'ァ-ヴ~ー', // カタカナ
  168 + 'ヲ-゚', // 半角カナ
  169 + '\!-\~', // 英数記号
  170 + );
  171 +
  172 + $reg = '['.implode(']+|[', $regs).']+';
  173 + $reg .= '|[^'.implode('', $regs).']+';
  174 +
  175 + self::$word_reg = $reg;
  176 + }
  177 + }
  178 +
  179 + private function getSeparatedWord($str){
  180 + preg_match_all('/'.self::$word_reg.'/iu', $str, $matches);
  181 + return $matches[0];
  182 + }
  183 +
  184 +
  185 + public function isDiffer()
  186 + {
  187 + return $this->is_differ;
  188 + }
  189 +
  190 + public function getData()
  191 + {
  192 + return $this->data;
  193 + }
  194 +
  195 + public function getHtml()
  196 + {
  197 + return $this->getTableHtml();
  198 + }
  199 +
  200 + public function getTableHtml()
  201 + {
  202 + if (empty($this->data)) {
  203 + return null;
  204 + } elseif(!empty($this->table_html)) {
  205 + return $this->table_html;
  206 + }
  207 +
  208 + $source = '<table class="differ -differ-table -differ-source">';
  209 + $change = '<table class="differ -differ-table -differ-change">';
  210 + foreach($this->data as $line) {
  211 + if (empty($line['line'])) {
  212 + continue;
  213 + }
  214 +
  215 + if (empty($line['differ'])) {
  216 + $html = '<tr class="-line -no-differ">';
  217 + $html .= '<td class="-number">'.$line['line'].'</td>';
  218 + $html .= '<td class="-text">'.(empty($line['source']) ? '&nbsp;' : $line['source']).'</td>';
  219 + $source .= $html;
  220 + $change .= $html;
  221 + } else {
  222 + $html = '<tr class="-line -is-differ">';
  223 + $html .= '<td class="-number">'.$line['line'].'</td>';
  224 + $html .= '<td class="-text">';
  225 + $source .= $html;
  226 + $change .= $html;
  227 +
  228 + foreach($line['words'] as $val) {
  229 + if (is_array($val) && array_key_exists('source', $val)) {
  230 + $source .= '<span class="-word -source">'.$val['source'].'</span>';
  231 + } else {
  232 + $source .= $val;
  233 + }
  234 + }
  235 +
  236 + foreach($line['words'] as $val) {
  237 + if (is_array($val) && array_key_exists('change', $val)) {
  238 + $change .= '<span class="-word -change">'.$val['change'].'</span>';
  239 + } else {
  240 + $change .= $val;
  241 + }
  242 + }
  243 +
  244 + $html = '</td>';
  245 + $source .= $html;
  246 + $change .= $html;
  247 + }
  248 +
  249 + $html = '</tr>';
  250 + $source .= $html;
  251 + $change .= $html;
  252 + }
  253 +
  254 + $html = '</table>';
  255 + $source .= $html;
  256 + $change .= $html;
  257 +
  258 + return $this->table_html = array(
  259 + 'source' => $source,
  260 + 'change' => $change,
  261 + );
  262 + }
  263 +
  264 + public function getListHtml()
  265 + {
  266 + if (empty($this->data)) {
  267 + return null;
  268 + } elseif(!empty($this->list_html)) {
  269 + return $this->list_html;
  270 + }
  271 +
  272 + $source = '<ol class="differ -differ-list -differ-source">';
  273 + $change = '<ol class="differ -differ-list -differ-change">';
  274 + foreach($this->data as $line) {
  275 + if (!$line['differ']) {
  276 + $html = '<li class="-line -no-differ">';
  277 + $html .= '<div class="-text">'.(empty($line['source']) ? '&nbsp;' : $line['source']).'</div>';
  278 + $source .= $html;
  279 + $change .= $html;
  280 + } else {
  281 + $html = '<li class="-line -is-differ">';
  282 + $html .= '<div class="-text">';
  283 + $source .= $html;
  284 + $change .= $html;
  285 +
  286 + foreach($line['words'] as $val) {
  287 + if (empty($val['change'])) {
  288 + $source .= $val;
  289 + } else {
  290 + $source .= '<span class="-word -source">'.$val['source'].'</span>';
  291 + }
  292 + }
  293 +
  294 + foreach($line['words'] as $val) {
  295 + if (empty($val['change'])) {
  296 + $change .= $val;
  297 + } else {
  298 + $change .= '<span class="-word -change">'.$val['change'].'</span>';
  299 + }
  300 + }
  301 +
  302 + $html = '</div>';
  303 + $html .= '</div>';
  304 + $source .= $html;
  305 + $change .= $html;
  306 + }
  307 +
  308 + $html = '</li>';
  309 + $source .= $html;
  310 + $change .= $html;
  311 + }
  312 +
  313 + $html = '</ol>';
  314 + $source .= $html;
  315 + $change .= $html;
  316 +
  317 + return $this->list_html = array(
  318 + 'source' => $source,
  319 + 'change' => $change,
  320 + );
  321 + }
  322 +}
No preview for this file type
  1 +{
  2 + "curly": true,
  3 + "bitwise": false,
  4 + "eqeqeq": true,
  5 + "es3": true,
  6 + "forin": true,
  7 + "freeze": true,
  8 + "immed": true,
  9 + "indent": 4,
  10 + "latedef": true,
  11 + "newcap": true,
  12 + "noarg": true,
  13 + "noempty": true,
  14 + "nonbsp": true,
  15 + "nonew": true,
  16 + "quotmark": true,
  17 + "undef": true,
  18 + "strict": true,
  19 + "globalstrict": true,
  20 + "browser": true,
  21 + "jquery": true,
  22 + "devel": true,
  23 + "globals": {
  24 + "iac": false
  25 + },
  26 + "predef": [ "_","JSON", "jsl", "jdd", "QUnit" ],
  27 + "maxparams": 14,
  28 + "maxdepth": 7,
  29 + "maxcomplexity": 15
  30 +}
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
  4 +
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "[]"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright [2015-2017] [Zack Grossbart]
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
  202 +
  203 +
  1 +JSON Diff
  2 +==================================================
  3 +
  4 +JSON Diff expands on the amazing work by the guys at [jsonlint.com](http://www.jsonlint.com) and provides a semantic compare tool for JSON documents.
  5 +
  6 +I often work with large documents and it's difficult to see the differences between them. Normal text compare tools work well for finding the differences in JavaScript code, but JSON data can have many differences in the text that don't actually change the data.
  7 +
  8 +JSON Diff sorts, formats, and compares two JSON documents to find the actual semantic differences instead of just the text ones.
  9 +
  10 +Try it out: [http://www.jsondiff.com](http://www.jsondiff.com)
  11 +
  12 +Run the built-in unit tests: [http://www.jsondiff.com/index-test.html](http://www.jsondiff.com/index-test.html)
No preview for this file type
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5 + <title>JSON Diff - The semantic JSON compare tool</title>
  6 +
  7 + <link rel="stylesheet" href="reset.css" type="text/css" media="screen">
  8 + <link rel="stylesheet" href="throbber.css" type="text/css" media="screen">
  9 + <link rel="stylesheet" href="qunit-2.1.1.css" type="text/css" media="screen">
  10 + <link rel="stylesheet" href="jdd.css" type="text/css" media="screen">
  11 +
  12 +
  13 + <script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  14 +
  15 + <script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
  16 + <script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
  17 + <script src="qunit-2.1.1.js" type="text/javascript" charset="utf-8"></script>
  18 + <script src="jdd.js" type="text/javascript" charset="utf-8"></script>
  19 + <script src="jdd_test.js" type="text/javascript" charset="utf-8"></script>
  20 +
  21 + <script>
  22 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  23 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  24 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  25 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  26 +
  27 + ga('create', 'UA-26336682-2', 'auto');
  28 + ga('send', 'pageview');
  29 +
  30 + </script>
  31 + </head>
  32 + <body>
  33 +
  34 + <div id="main">
  35 + <div class="header">
  36 + <h1>JSON Diff - Unit Tests</h1>
  37 + <h3>The semantic JSON compare tool</h3>
  38 +
  39 + <div class="weak">
  40 + <p>
  41 + Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
  42 + </p>
  43 +
  44 + <p>
  45 + Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
  46 + </p>
  47 +
  48 + <p>
  49 + Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
  50 + </p>
  51 +
  52 + </div>
  53 + </div>
  54 +
  55 + <div class="initContainer">
  56 + <div class="left">
  57 + <textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
  58 + <pre id="errorLeft" class="error"></pre>
  59 + <span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')"></span>
  60 + </div>
  61 +
  62 + <div class="center">
  63 + <button id="compare">Compare</button>
  64 + <div class="throbber-loader"></div>
  65 + <br/><br/><br/><br/>
  66 + or try some <a href="#" id="sample">sample data</a>
  67 +
  68 + </div>
  69 +
  70 + <div class="right">
  71 + <textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
  72 + <pre id="errorRight" class="error"></pre>
  73 + <span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')"></span>
  74 + </div>
  75 + </div>
  76 +
  77 + <div class="diffcontainer">
  78 + <div id="report">
  79 + </div>
  80 + <pre id="out" class="left" class="codeBlock"></pre>
  81 + <pre id="out2" class="right" class="codeBlock"></pre>
  82 + <ul id="toolbar" class="toolbar"></ul>
  83 + </div>
  84 +
  85 + <br />
  86 + <div id="qunit"></div>
  87 + <div id="qunit-fixture"></div>
  88 + </div>
  89 +
  90 + </body>
  91 +</html>
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5 + <title>JSON Diff - The semantic JSON compare tool</title>
  6 +
  7 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/reset.css" type="text/css" media="screen">
  8 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/throbber.css" type="text/css" media="screen">
  9 + <link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/jdd.css" type="text/css" media="screen">
  10 +
  11 + <script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  12 +
  13 + <script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
  14 + <script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
  15 + <script src="jdd.js" type="text/javascript" charset="utf-8"></script>
  16 +
  17 + <script>
  18 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  19 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  20 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  21 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  22 +
  23 + ga('create', 'UA-26336682-2', 'auto');
  24 + ga('send', 'pageview');
  25 +
  26 + </script>
  27 + </head>
  28 + <body>
  29 +
  30 + <div id="main">
  31 + <div class="header">
  32 + <h1>JSON Diff</h1>
  33 + <h3>The semantic JSON compare tool</h3>
  34 +
  35 + <div class="weak">
  36 + <p>
  37 + Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
  38 + </p>
  39 +
  40 + <p>
  41 + Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
  42 + </p>
  43 +
  44 + <p>
  45 + Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
  46 + </p>
  47 +
  48 + </div>
  49 + </div>
  50 +
  51 + <div class="initContainer">
  52 + <div class="left">
  53 + <textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1"></textarea>
  54 + <pre id="errorLeft" class="error"></pre>
  55 + <span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')" tabindex="4"></span>
  56 + </div>
  57 +
  58 + <div class="center">
  59 + <button id="compare" tabindex="3">Compare</button>
  60 + <div class="throbber-loader"></div>
  61 + <br/><br/>
  62 + or try some<br /><a href="#" id="sample">sample data</a>
  63 +
  64 + </div>
  65 +
  66 + <div class="right">
  67 + <textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2"></textarea>
  68 + <pre id="errorRight" class="error"></pre>
  69 + <span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')" tabindex="5"></span>
  70 + </div>
  71 + </div>
  72 +
  73 + <div class="diffcontainer">
  74 + <div id="report">
  75 + </div>
  76 + <pre id="out" class="left" class="codeBlock"></pre>
  77 + <pre id="out2" class="right" class="codeBlock"></pre>
  78 + <ul id="toolbar" class="toolbar"></ul>
  79 + </div>
  80 + </div>
  81 +
  82 + </body>
  83 +</html>
  1 +body {
  2 + padding: 0;
  3 + margin: 0;
  4 + font-family: Grotesk, "Helvetica Neue", Helvetica, sans-serif;
  5 + font-size: .9rem;
  6 +}
  7 +
  8 +h1 {
  9 + font-size: 3em;
  10 + line-height: 1;
  11 + margin-bottom: .1em;
  12 + font-weight: 300;
  13 +}
  14 +
  15 +h3 {
  16 + font-size: 1.5em;
  17 + line-height: 1;
  18 + margin-bottom: 1em;
  19 + margin-top: 1em;
  20 +}
  21 +
  22 +a {
  23 + text-decoration: none;
  24 + color: #36c;
  25 +}
  26 +
  27 +a:hover {
  28 + text-decoration: underline;
  29 +}
  30 +
  31 +#main {
  32 + margin: 25px auto;
  33 + width: 95%
  34 +}
  35 +
  36 +pre.codeBlock, div.codeBlock {
  37 + overflow:auto;
  38 + margin:0 0 1em;
  39 + padding:.5em 1em;
  40 +}
  41 +
  42 +pre.codeBlock .codeLine,
  43 +pre.codeBlock .line-number,
  44 +div.codeBlock .codeLine,
  45 +div.codeBlock .line-number {
  46 + font:normal normal 13px/16px monospace;
  47 + font-family: Monaco, monospace;
  48 + color:black;
  49 + display:block;
  50 +}
  51 +
  52 +pre.codeBlock.codeBlock span.code, div.codeBlock span.code {
  53 + min-width: 97%;
  54 + display: inline-block;
  55 +}
  56 +
  57 +pre.codeBlock .line-number, div.codeBlock .line-number {
  58 + margin:0 1em 0 -1em;
  59 + border-right:1px solid #eee;
  60 + color: #bbb;
  61 + text-align:right;
  62 + -webkit-user-select: none; /* Chrome/Safari */
  63 + -moz-user-select: none; /* Firefox */
  64 + -ms-user-select: none; /* IE10+ */
  65 +
  66 + -o-user-select: none;
  67 + user-select: none;
  68 + padding-left: 0.2em;
  69 + padding-right: 0.2em;
  70 +}
  71 +
  72 +pre.codeBlock .line-number span, div.codeBlock .line-number span {
  73 + display:block;
  74 + padding:0 .5em 0 1em;
  75 +}
  76 +
  77 +pre.codeBlock .cl {
  78 + display:block;
  79 + clear:both;
  80 +}
  81 +
  82 +div.diffcontainer {
  83 + position: relative;
  84 + margin: 0 auto;
  85 + display: none;
  86 +}
  87 +
  88 +div.left {
  89 + width: 40%;
  90 + display: inline-block;
  91 +}
  92 +
  93 +div.right {
  94 + width: 40%;
  95 + display: inline-block;
  96 + vertical-align: top;
  97 +}
  98 +
  99 +div.center {
  100 + width: 14%;
  101 + display: inline-block;
  102 + vertical-align: top;
  103 + text-align: left;
  104 + margin-top: 20%;
  105 + margin-left: 2%;
  106 + text-align: center;
  107 +}
  108 +
  109 +
  110 +pre.codeBlock.left {
  111 + width: 40%;
  112 + display: inline-block;
  113 + vertical-align: top;
  114 +}
  115 +
  116 +pre.codeBlock.right {
  117 + width: 40%;
  118 + display: inline-block;
  119 + vertical-align: top;
  120 + margin-left: 1em;
  121 +}
  122 +
  123 +ul.toolbar {
  124 + width: 11%;
  125 + display: inline-block;
  126 + right: 10px;
  127 + top: 5em;
  128 + position: absolute;
  129 +}
  130 +
  131 +#report .reportTitle {
  132 + border: thin solid lightgray;
  133 + border-radius: 4px;
  134 + margin-bottom: 1em;
  135 + padding: 5px;
  136 + background-color: rgba(50, 57, 88, 0.1);
  137 + color: black;
  138 + width: 15em;
  139 +}
  140 +
  141 +
  142 +ul.toolbar li {
  143 + border: thin solid lightgray;
  144 + border-radius: 4px;
  145 + margin-bottom: 1em;
  146 + padding: 5px;
  147 + background-color: rgba(50, 57, 88, 0.1);
  148 + color: black;
  149 + cursor: pointer;
  150 + line-height: 18px;
  151 +}
  152 +
  153 +ul.toolbar li code {
  154 + margin: 0 2px;
  155 + padding: 0px 5px;
  156 + border: 1px solid #eaeaea;
  157 + background-color: #f8f8f8;
  158 + border-radius: 3px;
  159 + color: #333;
  160 + line-height: 20px;
  161 + font-family: Monaco, monospace;
  162 + font-size: 0.85em;
  163 +}
  164 +
  165 +#buttons {
  166 + white-space: nowrap;
  167 + margin-bottom: 1em;
  168 +}
  169 +
  170 +#buttons a {
  171 + color: #8a6d3b;
  172 + font-size: 18px;
  173 +}
  174 +
  175 +#buttons a.disabled {
  176 + color: #999;
  177 + cursor: auto;
  178 +}
  179 +
  180 +#buttons a.disabled:hover {
  181 + text-decoration: none;
  182 + cursor: default;
  183 +}
  184 +
  185 +#prevNextLabel {
  186 + width: 80%;
  187 + text-align: center;
  188 + display: inline-block;
  189 +}
  190 +
  191 +div.gutter {
  192 + float: left;
  193 +}
  194 +
  195 +div.codeLine span.diff {
  196 + cursor: pointer;
  197 + color: #c00;
  198 +}
  199 +
  200 +div.codeLine span.eq_off, div.codeLine span.type_off, div.codeLine span.missing_off {
  201 + cursor: auto;
  202 +}
  203 +
  204 +div.codeLine span.eq {
  205 + background: rgba(50, 57, 88, 0.1);
  206 + color: #333;
  207 +}
  208 +
  209 +div.codeLine span.type {
  210 + background: rgba(220, 100, 100, 0.1);
  211 +}
  212 +
  213 +div.codeLine span.missing {
  214 + background: rgba(0, 160, 80, 0.1);
  215 + color: green;
  216 +}
  217 +
  218 +div.codeLine span.selected {
  219 + background: #cef;
  220 + color: #47e;
  221 +}
  222 +
  223 +canvas.center {
  224 + position: absolute;
  225 + left: 40%;
  226 + width: 20%;
  227 + height: 100%;
  228 + top: 0;
  229 +}
  230 +
  231 +textarea {
  232 + width: 100%;
  233 + min-height: 500px;
  234 + padding: 5px;
  235 + background: #f3f3f3;
  236 + border-radius: 4px;
  237 + resize: vertical;
  238 + white-space: pre;
  239 + word-wrap: normal;
  240 +}
  241 +
  242 +textarea.error {
  243 + border-color: #FBC2C4;
  244 +}
  245 +
  246 +pre.error {
  247 + background: #FBE3E4;
  248 + color: #D12F19;
  249 + border: thin solid #FBC2C4;
  250 + border-radius: 4px;
  251 + padding: 1em;
  252 + display: none;
  253 +}
  254 +
  255 +span.filterBlock {
  256 + margin-bottom: 1em;
  257 +}
  258 +
  259 +span.filterBlock input {
  260 + margin-left: 2em;
  261 +}
  262 +
  263 +#report {
  264 + margin-bottom: 1em;
  265 + position: relative;
  266 + height: 4em;
  267 +}
  268 +
  269 +#report button {
  270 + display: block;
  271 + position: absolute;
  272 + top: 0;
  273 + left: 56.5%;
  274 + border-color: #36c;
  275 + color: #36c;
  276 +}
  277 +
  278 +button {
  279 + font-size: 1.2em;
  280 + color: #333;
  281 + border: 1px solid #333;
  282 + border-radius: 4px;
  283 + background: white;
  284 + padding: .5rem .7rem;
  285 + cursor: pointer;
  286 +}
  287 +
  288 +.weak {
  289 + color: #333;
  290 + max-width: 25rem;
  291 + font-size: .9em;
  292 + position: absolute;
  293 + top: 29px;
  294 + left: 56.5%;
  295 + right: 3%;
  296 + padding: 0 1em 0 0;
  297 +}
  298 +
  299 +p {
  300 + margin-bottom: 1em;
  301 +}
  302 +
  303 +.header {
  304 + height: 8em;
  305 +}
  306 +
  307 +span.fileInput {
  308 + margin-left: 2em;
  309 + margin-top: 0.5em;
  310 + display: inline-block;
  311 +}
  312 +
  313 +.throbber-loader:not(:required) {
  314 + display: none;
  315 +}
  316 +
  317 +.progress .throbber-loader:not(:required) {
  318 + display: block;
  319 + margin: 5px auto;
  320 +}
  1 +/*******************************************************************************
  2 + *
  3 + * Copyright 2015-2019 Zack Grossbart
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + *
  17 + ******************************************************************************/
  18 +'use strict';
  19 +
  20 +// utilites
  21 +//
  22 +/**
  23 + * Fixing typeof
  24 + * takes value and returns type of value
  25 + * @param value
  26 + * return typeof value
  27 + */
  28 +function getType(value) {
  29 + if ((function () { return value && (value !== this); }).call(value)) {
  30 + //fallback on 'typeof' for truthy primitive values
  31 + return typeof value;
  32 + }
  33 + return ({}).toString.call(value).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  34 +}
  35 +/**
  36 + * Iterate over array of objects and call given callback for each item in the array
  37 + * Optionally may take this as scope
  38 + *
  39 + * @param array
  40 + * @param callback
  41 + * @param optional scope
  42 + */
  43 +function forEach(array, callback, scope) {
  44 + for (var idx = 0; idx < array.length; idx++) {
  45 + callback.call(scope, array[idx], idx, array);
  46 + }
  47 +}
  48 +
  49 +/**
  50 + * The jdd object handles all of the functions for the main page. It finds the diffs and manages
  51 + * the interactions of displaying them.
  52 + */
  53 +/*global jdd:true */
  54 +var jdd = {
  55 +
  56 + LEFT: 'left',
  57 + RIGHT: 'right',
  58 +
  59 + EQUALITY: 'eq',
  60 + TYPE: 'type',
  61 + MISSING: 'missing',
  62 + diffs: [],
  63 + requestCount: 0,
  64 +
  65 + /**
  66 + * Find the differences between the two objects and recurse into their sub objects.
  67 + */
  68 + findDiffs: function (/*Object*/ config1, /*Object*/ data1, /*Object*/ config2, /*Object*/ data2) {
  69 + config1.currentPath.push('/');
  70 + config2.currentPath.push('/');
  71 +
  72 + var key;
  73 + // no un-used vars
  74 + // var val;
  75 +
  76 + if (data1.length < data2.length) {
  77 + /*
  78 + * This means the second data has more properties than the first.
  79 + * We need to find the extra ones and create diffs for them.
  80 + */
  81 + for (key in data2) {
  82 + if (data2.hasOwnProperty(key)) {
  83 + // no un-used vars
  84 + // val = data1[key];
  85 + if (!data1.hasOwnProperty(key)) {
  86 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  87 + config2, jdd.generatePath(config2, '/' + key),
  88 + 'The right side of this object has more items than the left side', jdd.MISSING));
  89 + }
  90 + }
  91 + }
  92 + }
  93 +
  94 + /*
  95 + * Now we're going to look for all the properties in object one and
  96 + * compare them to object two
  97 + */
  98 + for (key in data1) {
  99 + if (data1.hasOwnProperty(key)) {
  100 + // no un-used vars
  101 + // val = data1[key];
  102 +
  103 + config1.currentPath.push(key);
  104 +
  105 + if (!data2.hasOwnProperty(key)) {
  106 + /*
  107 + * This means that the first data has a property which
  108 + * isn't present in the second data
  109 + */
  110 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  111 + config2, jdd.generatePath(config2),
  112 + 'Missing property <code>' + key + '</code> from the object on the right side', jdd.MISSING));
  113 + } else {
  114 + config2.currentPath.push(key);
  115 +
  116 + jdd.diffVal(data1[key], config1, data2[key], config2);
  117 + config2.currentPath.pop();
  118 + }
  119 + config1.currentPath.pop();
  120 + }
  121 + }
  122 +
  123 + config1.currentPath.pop();
  124 + config2.currentPath.pop();
  125 +
  126 + /*
  127 + * Now we want to look at all the properties in object two that
  128 + * weren't in object one and generate diffs for them.
  129 + */
  130 + for (key in data2) {
  131 + if (data2.hasOwnProperty(key)) {
  132 + // no un-used vars
  133 + // val = data1[key];
  134 +
  135 + if (!data1.hasOwnProperty(key)) {
  136 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  137 + config2, jdd.generatePath(config2, key),
  138 + 'Missing property <code>' + key + '</code> from the object on the left side', jdd.MISSING));
  139 + }
  140 + }
  141 + }
  142 + },
  143 +
  144 + /**
  145 + * Generate the differences between two values. This handles differences of object
  146 + * types and actual values.
  147 + */
  148 + diffVal: function (val1, config1, val2, config2) {
  149 +
  150 + if (getType(val1) === 'array') {
  151 + jdd.diffArray(val1, config1, val2, config2);
  152 + } else if (getType(val1) === 'object') {
  153 + if (['array', 'string', 'number', 'boolean', 'null'].indexOf(getType(val2)) > -1) {
  154 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  155 + config2, jdd.generatePath(config2),
  156 + 'Both types should be objects', jdd.TYPE));
  157 + } else {
  158 + jdd.findDiffs(config1, val1, config2, val2);
  159 + }
  160 + } else if (getType(val1) === 'string') {
  161 + if (getType(val2) !== 'string') {
  162 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  163 + config2, jdd.generatePath(config2),
  164 + 'Both types should be strings', jdd.TYPE));
  165 + } else if (val1 !== val2) {
  166 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  167 + config2, jdd.generatePath(config2),
  168 + 'Both sides should be equal strings', jdd.EQUALITY));
  169 + }
  170 + } else if (getType(val1) === 'number') {
  171 + if (getType(val2) !== 'number') {
  172 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  173 + config2, jdd.generatePath(config2),
  174 + 'Both types should be numbers', jdd.TYPE));
  175 + } else if (val1 !== val2) {
  176 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  177 + config2, jdd.generatePath(config2),
  178 + 'Both sides should be equal numbers', jdd.EQUALITY));
  179 + }
  180 + } else if (getType(val1) === 'boolean') {
  181 + jdd.diffBool(val1, config1, val2, config2);
  182 + } else if (getType(val1) === 'null' && getType(val2) !== 'null') {
  183 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  184 + config2, jdd.generatePath(config2),
  185 + 'Both types should be nulls', jdd.TYPE));
  186 + }
  187 + },
  188 +
  189 + /**
  190 + * Arrays are more complex because we need to recurse into them and handle different length
  191 + * issues so we handle them specially in this function.
  192 + */
  193 + diffArray: function (val1, config1, val2, config2) {
  194 + if (getType(val2) !== 'array') {
  195 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  196 + config2, jdd.generatePath(config2),
  197 + 'Both types should be arrays', jdd.TYPE));
  198 + return;
  199 + }
  200 +
  201 + if (val1.length < val2.length) {
  202 + /*
  203 + * Then there were more elements on the right side and we need to
  204 + * generate those differences.
  205 + */
  206 + for (var i = val1.length; i < val2.length; i++) {
  207 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  208 + config2, jdd.generatePath(config2, '[' + i + ']'),
  209 + 'Missing element <code>' + i + '</code> from the array on the left side', jdd.MISSING));
  210 + }
  211 + }
  212 + val1.forEach(function (arrayVal, index) {
  213 + if (val2.length <= index) {
  214 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1, '[' + index + ']'),
  215 + config2, jdd.generatePath(config2),
  216 + 'Missing element <code>' + index + '</code> from the array on the right side', jdd.MISSING));
  217 + } else {
  218 + config1.currentPath.push('/[' + index + ']');
  219 + config2.currentPath.push('/[' + index + ']');
  220 +
  221 + if (getType(val2) === 'array') {
  222 + /*
  223 + * If both sides are arrays then we want to diff them.
  224 + */
  225 + jdd.diffVal(val1[index], config1, val2[index], config2);
  226 + }
  227 + config1.currentPath.pop();
  228 + config2.currentPath.pop();
  229 + }
  230 + });
  231 + },
  232 +
  233 + /**
  234 + * We handle boolean values specially because we can show a nicer message for them.
  235 + */
  236 + diffBool: function (val1, config1, val2, config2) {
  237 + if (getType(val2) !== 'boolean') {
  238 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  239 + config2, jdd.generatePath(config2),
  240 + 'Both types should be booleans', jdd.TYPE));
  241 + } else if (val1 !== val2) {
  242 + if (val1) {
  243 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  244 + config2, jdd.generatePath(config2),
  245 + 'The left side is <code>true</code> and the right side is <code>false</code>', jdd.EQUALITY));
  246 + } else {
  247 + jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
  248 + config2, jdd.generatePath(config2),
  249 + 'The left side is <code>false</code> and the right side is <code>true</code>', jdd.EQUALITY));
  250 + }
  251 + }
  252 + },
  253 +
  254 + /**
  255 + * Format the object into the output stream and decorate the data tree with
  256 + * the data about this object.
  257 + */
  258 + formatAndDecorate: function (/*Object*/ config, /*Object*/ data) {
  259 + if (getType(data) === 'array') {
  260 + jdd.formatAndDecorateArray(config, data);
  261 + return;
  262 + }
  263 +
  264 + jdd.startObject(config);
  265 + config.currentPath.push('/');
  266 +
  267 + var props = jdd.getSortedProperties(data);
  268 +
  269 + /*
  270 + * If the first set has more than the second then we will catch it
  271 + * when we compare values. However, if the second has more then
  272 + * we need to catch that here.
  273 + */
  274 + props.forEach(function (key) {
  275 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '"' + jdd.unescapeString(key) + '": ';
  276 + config.currentPath.push(key);
  277 + config.paths.push({
  278 + path: jdd.generatePath(config),
  279 + line: config.line
  280 + });
  281 + jdd.formatVal(data[key], config);
  282 + config.currentPath.pop();
  283 + });
  284 +
  285 + jdd.finishObject(config);
  286 + config.currentPath.pop();
  287 + },
  288 +
  289 + /**
  290 + * Format the array into the output stream and decorate the data tree with
  291 + * the data about this object.
  292 + */
  293 + formatAndDecorateArray: function (/*Object*/ config, /*Array*/ data) {
  294 + jdd.startArray(config);
  295 +
  296 + /*
  297 + * If the first set has more than the second then we will catch it
  298 + * when we compare values. However, if the second has more then
  299 + * we need to catch that here.
  300 + */
  301 + data.forEach(function (arrayVal, index) {
  302 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
  303 + config.paths.push({
  304 + path: jdd.generatePath(config, '[' + index + ']'),
  305 + line: config.line
  306 + });
  307 +
  308 + config.currentPath.push('/[' + index + ']');
  309 + jdd.formatVal(arrayVal, config);
  310 + config.currentPath.pop();
  311 + });
  312 +
  313 + jdd.finishArray(config);
  314 + config.currentPath.pop();
  315 + },
  316 +
  317 + /**
  318 + * Generate the start of the an array in the output stream and push in the new path
  319 + */
  320 + startArray: function (config) {
  321 + config.indent++;
  322 + config.out += '[';
  323 +
  324 + if (config.paths.length === 0) {
  325 + /*
  326 + * Then we are at the top of the array and we want to add
  327 + * a path for it.
  328 + */
  329 + config.paths.push({
  330 + path: jdd.generatePath(config),
  331 + line: config.line
  332 + });
  333 + }
  334 +
  335 + if (config.indent === 0) {
  336 + config.indent++;
  337 + }
  338 + },
  339 +
  340 + /**
  341 + * Finish the array, outdent, and pop off all the path
  342 + */
  343 + finishArray: function (config) {
  344 + if (config.indent === 0) {
  345 + config.indent--;
  346 + }
  347 +
  348 + jdd.removeTrailingComma(config);
  349 +
  350 + config.indent--;
  351 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']';
  352 + if (config.indent !== 0) {
  353 + config.out += ',';
  354 + } else {
  355 + config.out += jdd.newLine(config);
  356 + }
  357 + },
  358 +
  359 + /**
  360 + * Generate the start of the an object in the output stream and push in the new path
  361 + */
  362 + startObject: function (config) {
  363 + config.indent++;
  364 + config.out += '{';
  365 +
  366 + if (config.paths.length === 0) {
  367 + /*
  368 + * Then we are at the top of the object and we want to add
  369 + * a path for it.
  370 + */
  371 + config.paths.push({
  372 + path: jdd.generatePath(config),
  373 + line: config.line
  374 + });
  375 + }
  376 +
  377 + if (config.indent === 0) {
  378 + config.indent++;
  379 + }
  380 + },
  381 +
  382 + /**
  383 + * Finish the object, outdent, and pop off all the path
  384 + */
  385 + finishObject: function (config) {
  386 + if (config.indent === 0) {
  387 + config.indent--;
  388 + }
  389 +
  390 + jdd.removeTrailingComma(config);
  391 +
  392 + config.indent--;
  393 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '}';
  394 + if (config.indent !== 0) {
  395 + config.out += ',';
  396 + } else {
  397 + config.out += jdd.newLine(config);
  398 + }
  399 + },
  400 +
  401 + /**
  402 + * Format a specific value into the output stream.
  403 + */
  404 + formatVal: function (val, config) {
  405 + if (getType(val) === 'array') {
  406 + config.out += '[';
  407 +
  408 + config.indent++;
  409 + val.forEach(function (arrayVal, index) {
  410 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
  411 + config.paths.push({
  412 + path: jdd.generatePath(config, '[' + index + ']'),
  413 + line: config.line
  414 + });
  415 +
  416 + config.currentPath.push('/[' + index + ']');
  417 + jdd.formatVal(arrayVal, config);
  418 + config.currentPath.pop();
  419 + });
  420 + jdd.removeTrailingComma(config);
  421 + config.indent--;
  422 +
  423 + config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']' + ',';
  424 + } else if (getType(val) === 'object') {
  425 + jdd.formatAndDecorate(config, val);
  426 + } else if (getType(val) === 'string') {
  427 + config.out += '"' + jdd.unescapeString(val) + '",';
  428 + } else if (getType(val) === 'number') {
  429 + config.out += val + ',';
  430 + } else if (getType(val) === 'boolean') {
  431 + config.out += val + ',';
  432 + } else if (getType(val) === 'null') {
  433 + config.out += 'null,';
  434 + }
  435 + },
  436 +
  437 + /**
  438 + * When we parse the JSON string we end up removing the escape strings when we parse it
  439 + * into objects. This results in invalid JSON if we insert those strings back into the
  440 + * generated JSON. We also need to look out for characters that change the line count
  441 + * like new lines and carriage returns.
  442 + *
  443 + * This function puts those escaped values back when we generate the JSON output for the
  444 + * well known escape strings in JSON. It handles properties and values.
  445 + *
  446 + * This function does not handle unicode escapes. Unicode escapes are optional in JSON
  447 + * and the JSON output is still valid with a unicode character in it.
  448 + */
  449 + unescapeString: function (val) {
  450 + if (val) {
  451 + return val.replace('\\', '\\\\') // Single slashes need to be replaced first
  452 + .replace(/\"/g, '\\"') // Then double quotes
  453 + .replace(/\n/g, '\\n') // New lines
  454 + .replace('\b', '\\b') // Backspace
  455 + .replace(/\f/g, '\\f') // Formfeed
  456 + .replace(/\r/g, '\\r') // Carriage return
  457 + .replace(/\t/g, '\\t'); // Horizontal tabs
  458 + } else {
  459 + return val;
  460 + }
  461 + },
  462 +
  463 + /**
  464 + * Generate a JSON path based on the specific configuration and an optional property.
  465 + */
  466 + generatePath: function (config, prop) {
  467 + var s = '';
  468 + config.currentPath.forEach(function (path) {
  469 + s += path;
  470 + });
  471 +
  472 + if (prop) {
  473 + s += '/' + prop;
  474 + }
  475 +
  476 + if (s.length === 0) {
  477 + return '/';
  478 + } else {
  479 + return s;
  480 + }
  481 + },
  482 +
  483 + /**
  484 + * Add a new line to the output stream
  485 + */
  486 + newLine: function (config) {
  487 + config.line++;
  488 + return '\n';
  489 + },
  490 +
  491 + /**
  492 + * Sort all the relevant properties and return them in an alphabetical sort by property key
  493 + */
  494 + getSortedProperties: function (/*Object*/ obj) {
  495 + var props = [];
  496 +
  497 + for (var prop in obj) {
  498 + if (obj.hasOwnProperty(prop)) {
  499 + props.push(prop);
  500 + }
  501 + }
  502 +
  503 + props = props.sort(function (a, b) {
  504 + return a.localeCompare(b);
  505 + });
  506 +
  507 + return props;
  508 + },
  509 +
  510 + /**
  511 + * Generate the diff and verify that it matches a JSON path
  512 + */
  513 + generateDiff: function (config1, path1, config2, path2, /*String*/ msg, type) {
  514 + if (path1 !== '/' && path1.charAt(path1.length - 1) === '/') {
  515 + path1 = path1.substring(0, path1.length - 1);
  516 + }
  517 +
  518 + if (path2 !== '/' && path2.charAt(path2.length - 1) === '/') {
  519 + path2 = path2.substring(0, path2.length - 1);
  520 + }
  521 + var pathObj1 = config1.paths.find(function (path) {
  522 + return path.path === path1;
  523 + });
  524 + var pathObj2 = config2.paths.find(function (path) {
  525 + return path.path === path2;
  526 + });
  527 +
  528 + if (!pathObj1) {
  529 + throw 'Unable to find line number for (' + msg + '): ' + path1;
  530 + }
  531 +
  532 + if (!pathObj2) {
  533 + throw 'Unable to find line number for (' + msg + '): ' + path2;
  534 + }
  535 +
  536 + return {
  537 + path1: pathObj1,
  538 + path2: pathObj2,
  539 + type: type,
  540 + msg: msg
  541 + };
  542 + },
  543 +
  544 + /**
  545 + * Get the current indent level
  546 + */
  547 + getTabs: function (/*int*/ indent) {
  548 + var s = '';
  549 + for (var i = 0; i < indent; i++) {
  550 + s += ' ';
  551 + }
  552 +
  553 + return s;
  554 + },
  555 +
  556 + /**
  557 + * Remove the trailing comma from the output.
  558 + */
  559 + removeTrailingComma: function (config) {
  560 + /*
  561 + * Remove the trailing comma
  562 + */
  563 + if (config.out.charAt(config.out.length - 1) === ',') {
  564 + config.out = config.out.substring(0, config.out.length - 1);
  565 + }
  566 + },
  567 +
  568 + /**
  569 + * Create a config object for holding differences
  570 + */
  571 + createConfig: function () {
  572 + return {
  573 + out: '',
  574 + indent: -1,
  575 + currentPath: [],
  576 + paths: [],
  577 + line: 1
  578 + };
  579 + },
  580 +
  581 + /**
  582 + * Format the output pre tags.
  583 + */
  584 + formatPRETags: function () {
  585 + forEach($('pre'), function (pre) {
  586 + var codeBlock = $('<pre class="codeBlock"></pre>');
  587 + var lineNumbers = $('<div class="gutter"></div>');
  588 + codeBlock.append(lineNumbers);
  589 +
  590 + var codeLines = $('<div></div>');
  591 + codeBlock.append(codeLines);
  592 +
  593 + var addLine = function (line, index) {
  594 + var div = $('<div class="codeLine line' + (index + 1) + '"></div>');
  595 + lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
  596 +
  597 + var span = $('<span class="code"></span');
  598 + span.text(line);
  599 + div.append(span);
  600 +
  601 + codeLines.append(div);
  602 + };
  603 +
  604 + var lines = $(pre).text().split('\n');
  605 + lines.forEach(addLine);
  606 +
  607 + codeBlock.addClass($(pre).attr('class'));
  608 + codeBlock.attr('id', $(pre).attr('id'));
  609 +
  610 + $(pre).replaceWith(codeBlock);
  611 + });
  612 + },
  613 +
  614 + /**
  615 + * Format the text edits which handle the JSON input
  616 + */
  617 + formatTextAreas: function () {
  618 + forEach($('textarea'), function (textarea) {
  619 + var codeBlock = $('<div class="codeBlock"></div>');
  620 + var lineNumbers = $('<div class="gutter"></div>');
  621 + codeBlock.append(lineNumbers);
  622 +
  623 + var addLine = function (line, index) {
  624 + lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
  625 + };
  626 +
  627 + var lines = $(textarea).val().split('\n');
  628 + lines.forEach(addLine);
  629 +
  630 + $(textarea).replaceWith(codeBlock);
  631 + codeBlock.append(textarea);
  632 + });
  633 + },
  634 +
  635 + handleDiffClick: function (line, side) {
  636 + var diffs = jdd.diffs.filter(function (diff) {
  637 + if (side === jdd.LEFT) {
  638 + return line === diff.path1.line;
  639 + } else if (side === jdd.RIGHT) {
  640 + return line === diff.path2.line;
  641 + } else {
  642 + return line === diff.path1.line || line === diff.path2.line;
  643 + }
  644 + });
  645 +
  646 + $('pre.left span.code').removeClass('selected');
  647 + $('pre.right span.code').removeClass('selected');
  648 + $('ul.toolbar').text('');
  649 + diffs.forEach(function (diff) {
  650 + $('pre.left div.line' + diff.path1.line + ' span.code').addClass('selected');
  651 + $('pre.right div.line' + diff.path2.line + ' span.code').addClass('selected');
  652 + });
  653 +
  654 + if (side === jdd.LEFT || side === jdd.RIGHT) {
  655 + jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
  656 + return diff.path1.line === line;
  657 + });
  658 + }
  659 +
  660 + if (jdd.currentDiff === -1) {
  661 + jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
  662 + return diff.path2.line === line;
  663 + });
  664 + }
  665 +
  666 + var buttons = $('<div id="buttons"><div>');
  667 + var prev = $('<a href="#" title="Previous difference" id="prevButton">&lt;</a>');
  668 + prev.addClass('disabled');
  669 + prev.click(function (e) {
  670 + e.preventDefault();
  671 + jdd.highlightPrevDiff();
  672 + });
  673 + buttons.append(prev);
  674 +
  675 + buttons.append('<span id="prevNextLabel"></span>');
  676 +
  677 + var next = $('<a href="#" title="Next difference" id="nextButton">&gt;</a>');
  678 + next.click(function (e) {
  679 + e.preventDefault();
  680 + jdd.highlightNextDiff();
  681 + });
  682 + buttons.append(next);
  683 +
  684 + $('ul.toolbar').append(buttons);
  685 + jdd.updateButtonStyles();
  686 +
  687 + jdd.showDiffDetails(diffs);
  688 + },
  689 +
  690 + highlightPrevDiff: function () {
  691 + if (jdd.currentDiff > 0) {
  692 + jdd.currentDiff--;
  693 + jdd.highlightDiff(jdd.currentDiff);
  694 + jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
  695 +
  696 + jdd.updateButtonStyles();
  697 + }
  698 + },
  699 +
  700 + highlightNextDiff: function () {
  701 + if (jdd.currentDiff < jdd.diffs.length - 1) {
  702 + jdd.currentDiff++;
  703 + jdd.highlightDiff(jdd.currentDiff);
  704 + jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
  705 +
  706 + jdd.updateButtonStyles();
  707 + }
  708 + },
  709 +
  710 + updateButtonStyles: function () {
  711 + $('#prevButton').removeClass('disabled');
  712 + $('#nextButton').removeClass('disabled');
  713 +
  714 + $('#prevNextLabel').text((jdd.currentDiff + 1) + ' of ' + (jdd.diffs.length));
  715 +
  716 + if (jdd.currentDiff === 1) {
  717 + $('#prevButton').addClass('disabled');
  718 + } else if (jdd.currentDiff === jdd.diffs.length - 1) {
  719 + $('#nextButton').addClass('disabled');
  720 + }
  721 + },
  722 +
  723 + /**
  724 + * Highlight the diff at the specified index
  725 + */
  726 + highlightDiff: function (index) {
  727 + jdd.handleDiffClick(jdd.diffs[index].path1.line, jdd.BOTH);
  728 + },
  729 +
  730 + /**
  731 + * Show the details of the specified diff
  732 + */
  733 + showDiffDetails: function (diffs) {
  734 + diffs.forEach(function (diff) {
  735 + var li = $('<li></li>');
  736 + li.html(diff.msg);
  737 + $('ul.toolbar').append(li);
  738 +
  739 + li.click(function () {
  740 + jdd.scrollToDiff(diff);
  741 + });
  742 +
  743 + });
  744 + },
  745 +
  746 + /**
  747 + * Scroll the specified diff to be visible
  748 + */
  749 + scrollToDiff: function (diff) {
  750 + $('html, body').animate({
  751 + scrollTop: $('pre.left div.line' + diff.path1.line + ' span.code').offset().top
  752 + }, 0);
  753 + },
  754 +
  755 + /**
  756 + * Process the specified diff
  757 + */
  758 + processDiffs: function () {
  759 + var left = [];
  760 + var right = [];
  761 + jdd.diffs.forEach(function (diff) {
  762 + $('pre.left div.line' + diff.path1.line + ' span.code').addClass(diff.type).addClass('diff');
  763 + if (left.indexOf(diff.path1.line) === -1) {
  764 + $('pre.left div.line' + diff.path1.line + ' span.code').click(function () {
  765 + jdd.handleDiffClick(diff.path1.line, jdd.LEFT);
  766 + });
  767 + left.push(diff.path1.line);
  768 + }
  769 +
  770 + $('pre.right div.line' + diff.path2.line + ' span.code').addClass(diff.type).addClass('diff');
  771 + if (right.indexOf(diff.path2.line) === -1) {
  772 + $('pre.right div.line' + diff.path2.line + ' span.code').click(function () {
  773 + jdd.handleDiffClick(diff.path2.line, jdd.RIGHT);
  774 + });
  775 + right.push(diff.path2.line);
  776 + }
  777 + });
  778 +
  779 + jdd.diffs = jdd.diffs.sort(function (a, b) {
  780 + return a.path1.line - b.path1.line;
  781 + });
  782 +
  783 + },
  784 +
  785 + /**
  786 + * Validate the input against the JSON parser
  787 + */
  788 + validateInput: function (json, side) {
  789 + try {
  790 + jsl.parser.parse(json);
  791 +
  792 + if (side === jdd.LEFT) {
  793 + $('#errorLeft').text('').hide();
  794 + $('#textarealeft').removeClass('error');
  795 + } else {
  796 + $('#errorRight').text('').hide();
  797 + $('#textarearight').removeClass('error');
  798 + }
  799 +
  800 + return true;
  801 + } catch (parseException) {
  802 + if (side === jdd.LEFT) {
  803 + $('#errorLeft').text(parseException.message).show();
  804 + $('#textarealeft').addClass('error');
  805 + } else {
  806 + $('#errorRight').text(parseException.message).show();
  807 + $('#textarearight').addClass('error');
  808 + }
  809 + return false;
  810 + }
  811 + },
  812 +
  813 + /**
  814 + * Handle the file uploads
  815 + */
  816 + handleFiles: function (files, side) {
  817 + var reader = new FileReader();
  818 +
  819 + reader.onload = (function () {
  820 + return function (e) {
  821 + if (side === jdd.LEFT) {
  822 + $('#textarealeft').val(e.target.result);
  823 + } else {
  824 + $('#textarearight').val(e.target.result);
  825 + }
  826 + };
  827 + })(files[0]);
  828 +
  829 + reader.readAsText(files[0]);
  830 + },
  831 +
  832 + setupNewDiff: function () {
  833 + $('div.initContainer').show();
  834 + $('div.diffcontainer').hide();
  835 + $('div.diffcontainer pre').text('');
  836 + $('ul.toolbar').text('');
  837 + },
  838 +
  839 + /**
  840 + * Generate the report section with the diff
  841 + */
  842 + generateReport: function () {
  843 + var report = $('#report');
  844 +
  845 + report.text('');
  846 +
  847 + var newDiff = $('<button>Perform a new diff</button>');
  848 + report.append(newDiff);
  849 + newDiff.click(function () {
  850 + jdd.setupNewDiff();
  851 + });
  852 +
  853 + if (jdd.diffs.length === 0) {
  854 + report.append('<span>The two files were semantically identical.</span>');
  855 + return;
  856 + }
  857 +
  858 + var typeCount = 0;
  859 + var eqCount = 0;
  860 + var missingCount = 0;
  861 + jdd.diffs.forEach(function (diff) {
  862 + if (diff.type === jdd.EQUALITY) {
  863 + eqCount++;
  864 + } else if (diff.type === jdd.MISSING) {
  865 + missingCount++;
  866 + } else if (diff.type === jdd.TYPE) {
  867 + typeCount++;
  868 + }
  869 + });
  870 +
  871 + var title = $('<div class="reportTitle"></div>');
  872 + if (jdd.diffs.length === 1) {
  873 + title.text('Found ' + (jdd.diffs.length) + ' difference');
  874 + } else {
  875 + title.text('Found ' + (jdd.diffs.length) + ' differences');
  876 + }
  877 +
  878 + report.prepend(title);
  879 +
  880 + var filterBlock = $('<span class="filterBlock">Show:</span>');
  881 +
  882 + /*
  883 + * The missing checkbox
  884 + */
  885 + if (missingCount > 0) {
  886 + var missing = $('<label><input id="showMissing" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  887 + if (missingCount === 1) {
  888 + missing.append(missingCount + ' missing property');
  889 + } else {
  890 + missing.append(missingCount + ' missing properties');
  891 + }
  892 + missing.children('input').click(function () {
  893 + if (!$(this).prop('checked')) {
  894 + $('span.code.diff.missing').addClass('missing_off').removeClass('missing');
  895 + } else {
  896 + $('span.code.diff.missing_off').addClass('missing').removeClass('missing_off');
  897 + }
  898 + });
  899 + filterBlock.append(missing);
  900 + }
  901 +
  902 + /*
  903 + * The types checkbox
  904 + */
  905 + if (typeCount > 0) {
  906 + var types = $('<label><input id="showTypes" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  907 + if (typeCount === 1) {
  908 + types.append(typeCount + ' incorrect type');
  909 + } else {
  910 + types.append(typeCount + ' incorrect types');
  911 + }
  912 +
  913 + types.children('input').click(function () {
  914 + if (!$(this).prop('checked')) {
  915 + $('span.code.diff.type').addClass('type_off').removeClass('type');
  916 + } else {
  917 + $('span.code.diff.type_off').addClass('type').removeClass('type_off');
  918 + }
  919 + });
  920 + filterBlock.append(types);
  921 + }
  922 +
  923 + /*
  924 + * The equals checkbox
  925 + */
  926 + if (eqCount > 0) {
  927 + var eq = $('<label><input id="showEq" type="checkbox" name="checkbox" value="value" checked="true"></label>');
  928 + if (eqCount === 1) {
  929 + eq.append(eqCount + ' unequal value');
  930 + } else {
  931 + eq.append(eqCount + ' unequal values');
  932 + }
  933 + eq.children('input').click(function () {
  934 + if (!$(this).prop('checked')) {
  935 + $('span.code.diff.eq').addClass('eq_off').removeClass('eq');
  936 + } else {
  937 + $('span.code.diff.eq_off').addClass('eq').removeClass('eq_off');
  938 + }
  939 + });
  940 + filterBlock.append(eq);
  941 + }
  942 +
  943 + report.append(filterBlock);
  944 +
  945 +
  946 + },
  947 +
  948 + /**
  949 + * Implement the compare button and complete the compare process
  950 + */
  951 + compare: function () {
  952 +
  953 + if (jdd.requestCount !== 0) {
  954 + /*
  955 + * This means we have a pending request and we just need to wait for that to finish.
  956 + */
  957 + return;
  958 + }
  959 +
  960 + $('body').addClass('progress');
  961 + $('#compare').prop('disabled', true);
  962 +
  963 + var loadUrl = function (id, errId) {
  964 + if ($('#' + id).val().trim().substring(0, 4).toLowerCase() === 'http') {
  965 + jdd.requestCount++;
  966 + $.post('proxy.php',
  967 + {
  968 + 'url': $('#' + id).val().trim()
  969 + }, function (responseObj) {
  970 + if (responseObj.error) {
  971 + $('#' + errId).text(responseObj.result).show();
  972 + $('#' + id).addClass('error');
  973 + $('body').removeClass('progress');
  974 + $('#compare').prop('disabled', false);
  975 + } else {
  976 + $('#' + id).val(responseObj.content);
  977 + jdd.requestCount--;
  978 + jdd.compare();
  979 + }
  980 + }, 'json');
  981 + return true;
  982 + } else {
  983 + return false;
  984 + }
  985 + };
  986 +
  987 + if (loadUrl('textarealeft', 'errorLeft')) {
  988 + return;
  989 + }
  990 +
  991 + if (loadUrl('textarearight', 'errorRight')) {
  992 + return;
  993 + }
  994 +
  995 + /*
  996 + * We'll start by running the text through JSONlint since it gives
  997 + * much better error messages.
  998 + */
  999 + var leftValid = jdd.validateInput($('#textarealeft').val(), jdd.LEFT);
  1000 + var rightValid = jdd.validateInput($('#textarearight').val(), jdd.RIGHT);
  1001 +
  1002 + if (!leftValid || !rightValid) {
  1003 + $('body').removeClass('progress');
  1004 + $('#compare').prop('disabled', false);
  1005 + return;
  1006 + }
  1007 +
  1008 + $('div.initContainer').hide();
  1009 + $('div.diffcontainer').show();
  1010 +
  1011 + jdd.diffs = [];
  1012 +
  1013 + var left = JSON.parse($('#textarealeft').val());
  1014 + var right = JSON.parse($('#textarearight').val());
  1015 +
  1016 +
  1017 + var config = jdd.createConfig();
  1018 + jdd.formatAndDecorate(config, left);
  1019 + $('#out').text(config.out);
  1020 +
  1021 + var config2 = jdd.createConfig();
  1022 + jdd.formatAndDecorate(config2, right);
  1023 + $('#out2').text(config2.out);
  1024 +
  1025 + jdd.formatPRETags();
  1026 +
  1027 + config.currentPath = [];
  1028 + config2.currentPath = [];
  1029 +
  1030 + jdd.diffVal(left, config, right, config2);
  1031 + jdd.processDiffs();
  1032 + jdd.generateReport();
  1033 +
  1034 + //console.log('diffs: ' + JSON.stringify(jdd.diffs));
  1035 +
  1036 + if (jdd.diffs.length > 0) {
  1037 + jdd.highlightDiff(0);
  1038 + jdd.currentDiff = 0;
  1039 + jdd.updateButtonStyles();
  1040 + }
  1041 +
  1042 + $('body').removeClass('progress');
  1043 + $('#compare').prop('disabled', false);
  1044 +
  1045 + /*
  1046 + * We want to switch the toolbar bar between fixed and absolute position when you
  1047 + * scroll so you can get the maximum number of toolbar items.
  1048 + */
  1049 + var toolbarTop = $('#toolbar').offset().top - 15;
  1050 + $(window).scroll(function () {
  1051 + if (toolbarTop < $(window).scrollTop()) {
  1052 + $('#toolbar').css('position', 'fixed').css('top', '10px');
  1053 + } else {
  1054 + $('#toolbar').css('position', 'absolute').css('top', '');
  1055 + }
  1056 + });
  1057 +
  1058 + },
  1059 +
  1060 + /**
  1061 + * Load in the sample data
  1062 + */
  1063 + loadSampleData: function () {
  1064 + $('#textarealeft').val('{"Aidan Gillen": {"array": ["Game of Thron\\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}');
  1065 + /*$('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  1066 + $('#textarearight').val('{"foo":[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');*/
  1067 + $('#textarearight').val('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}');
  1068 + },
  1069 +
  1070 + getParameterByName: function (name) {
  1071 + name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
  1072 + var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
  1073 + results = regex.exec(location.search);
  1074 + return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
  1075 + }
  1076 +};
  1077 +
  1078 +
  1079 +
  1080 +jQuery(document).ready(function () {
  1081 + $('#compare').click(function () {
  1082 + jdd.compare();
  1083 + });
  1084 +
  1085 + if (jdd.getParameterByName('left')) {
  1086 + $('#textarealeft').val(jdd.getParameterByName('left'));
  1087 + }
  1088 +
  1089 + if (jdd.getParameterByName('right')) {
  1090 + $('#textarearight').val(jdd.getParameterByName('right'));
  1091 + }
  1092 +
  1093 + if (jdd.getParameterByName('left') && jdd.getParameterByName('right')) {
  1094 + jdd.compare();
  1095 + }
  1096 +
  1097 +
  1098 + $('#sample').click(function (e) {
  1099 + e.preventDefault();
  1100 + jdd.loadSampleData();
  1101 + });
  1102 +
  1103 + $(document).keydown(function (event) {
  1104 + if (event.keyCode === 78 || event.keyCode === 39) {
  1105 + /*
  1106 + * The N key or right arrow key
  1107 + */
  1108 + jdd.highlightNextDiff();
  1109 + } else if (event.keyCode === 80 || event.keyCode === 37) {
  1110 + /*
  1111 + * The P key or left arrow key
  1112 + */
  1113 + jdd.highlightPrevDiff();
  1114 + }
  1115 + });
  1116 +});
  1117 +
  1118 +// polyfills
  1119 +
  1120 +// Array.prototype.find
  1121 +// https://tc39.github.io/ecma262/#sec-array.prototype.find
  1122 +if (!Array.prototype.find) {
  1123 + Object.defineProperty(Array.prototype, 'find', {
  1124 + value: function (predicate) {
  1125 + // 1. Let O be ? ToObject(this value).
  1126 + if (this === null) {
  1127 + throw new TypeError('"this" is null or not defined');
  1128 + }
  1129 +
  1130 + var o = Object(this);
  1131 +
  1132 + // 2. Let len be ? ToLength(? Get(O, "length")).
  1133 + var len = o.length >>> 0;
  1134 +
  1135 + // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  1136 + if (typeof predicate !== 'function') {
  1137 + throw new TypeError('predicate must be a function');
  1138 + }
  1139 +
  1140 + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  1141 + var thisArg = arguments[1];
  1142 +
  1143 + // 5. Let k be 0.
  1144 + var k = 0;
  1145 +
  1146 + // 6. Repeat, while k < len
  1147 + while (k < len) {
  1148 + // a. Let Pk be ! ToString(k).
  1149 + // b. Let kValue be ? Get(O, Pk).
  1150 + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
  1151 + // d. If testResult is true, return kValue.
  1152 + var kValue = o[k];
  1153 + if (predicate.call(thisArg, kValue, k, o)) {
  1154 + return kValue;
  1155 + }
  1156 + // e. Increase k by 1.
  1157 + k++;
  1158 + }
  1159 +
  1160 + // 7. Return undefined.
  1161 + return undefined;
  1162 + },
  1163 + configurable: true,
  1164 + writable: true
  1165 + });
  1166 +}
  1167 +
  1168 +// Array.prototype.findIndex
  1169 +// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
  1170 +if (!Array.prototype.findIndex) {
  1171 + Object.defineProperty(Array.prototype, 'findIndex', {
  1172 + value: function (predicate) {
  1173 + // 1. Let O be ? ToObject(this value).
  1174 + if (this === null) {
  1175 + throw new TypeError('"this" is null or not defined');
  1176 + }
  1177 +
  1178 + var o = Object(this);
  1179 +
  1180 + // 2. Let len be ? ToLength(? Get(O, "length")).
  1181 + var len = o.length >>> 0;
  1182 +
  1183 + // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  1184 + if (typeof predicate !== 'function') {
  1185 + throw new TypeError('predicate must be a function');
  1186 + }
  1187 +
  1188 + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  1189 + var thisArg = arguments[1];
  1190 +
  1191 + // 5. Let k be 0.
  1192 + var k = 0;
  1193 +
  1194 + // 6. Repeat, while k < len
  1195 + while (k < len) {
  1196 + // a. Let Pk be ! ToString(k).
  1197 + // b. Let kValue be ? Get(O, Pk).
  1198 + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
  1199 + // d. If testResult is true, return k.
  1200 + var kValue = o[k];
  1201 + if (predicate.call(thisArg, kValue, k, o)) {
  1202 + return k;
  1203 + }
  1204 + // e. Increase k by 1.
  1205 + k++;
  1206 + }
  1207 +
  1208 + // 7. Return -1.
  1209 + return -1;
  1210 + },
  1211 + configurable: true,
  1212 + writable: true
  1213 + });
  1214 +}
  1 +/*******************************************************************************
  2 + *
  3 + * Copyright 2015-2017 Zack Grossbart
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + *
  17 + ******************************************************************************/
  18 +'use strict';
  19 +
  20 +/**
  21 + * This is the main test harness for JSONDiff. It runs unit tests and integrations tests in the browser.
  22 + * To run these tests just access the index-test.html page. You can run the tests on the live site at
  23 + * http://www.jsondiff.com/index-test.html.
  24 + *
  25 + * Each test follows the same basic pattern: set the text of the left and right text areas then run the
  26 + * jdd.compare function. Once the compare completes we examine the JSON data in the jdd.diffs object to
  27 + * determine if the test completed successfully. As a last step we clear out the two text areas and call
  28 + * jdd.setupNewDiff to prepare for the next test.
  29 + */
  30 +
  31 +/**
  32 + * This is the basic compate test. It does a compare of our sample data and makes sure that we have the
  33 + * correct number of diffs of each type. It also checks a few random diffs to make sure they are correct.
  34 + */
  35 +QUnit.test('Object compare tests', function (assert) {
  36 + $('#textarealeft').val('{"Aidan Gillen": {"array": ["Game of Thron\\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true, "null": null, "a_null": null, "another_null": "null check", "object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}');
  37 + $('#textarearight').val('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false, "null": null, "a_null":88, "another_null": null, "object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}');
  38 +
  39 + jdd.compare();
  40 +
  41 + // This test makes sure there wasn't a parsing error
  42 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  43 +
  44 + assert.ok(jdd.diffs.length === 20, 'Checking for the correct number of differences');
  45 +
  46 + var typeCount = 0;
  47 + var eqCount = 0;
  48 + var missingCount = 0;
  49 +
  50 + jdd.diffs.forEach(function (diff) {
  51 + if (diff.type === jdd.EQUALITY) {
  52 + eqCount++;
  53 + } else if (diff.type === jdd.MISSING) {
  54 + missingCount++;
  55 + } else if (diff.type === jdd.TYPE) {
  56 + typeCount++;
  57 + }
  58 + });
  59 +
  60 + assert.ok(eqCount === 4, 'Checking unequal values');
  61 + assert.ok(missingCount === 11, 'Checking missing values');
  62 + assert.ok(typeCount === 5, 'Checking incorrect types');
  63 +
  64 + $('#textarealeft').val('');
  65 + $('#textarearight').val('');
  66 + jdd.setupNewDiff();
  67 +});
  68 +
  69 +/**
  70 + * This test focuses on finding differences between objects and arrays with similar data at the
  71 + * root of the JSON document.
  72 + */
  73 +QUnit.test('Array to object compare tests', function (assert) {
  74 + $('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  75 + $('#textarearight').val('{"foo":[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');
  76 +
  77 + jdd.compare();
  78 +
  79 + // This test makes sure there wasn't a parsing error
  80 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  81 +
  82 + assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
  83 +
  84 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
  85 +
  86 + $('#textarealeft').val('');
  87 + $('#textarearight').val('');
  88 + jdd.setupNewDiff();
  89 +});
  90 +
  91 +/**
  92 + * This test focuses on comparing arrays and makes sure that we can properly tell the difference
  93 + * between missing and unequal values.
  94 + */
  95 +QUnit.test('Array compare tests', function (assert) {
  96 + $('#textarealeft').val('[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  97 + $('#textarearight').val('[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  98 +
  99 + jdd.compare();
  100 +
  101 + // This test makes sure there wasn't a parsing error
  102 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  103 +
  104 + assert.ok(jdd.diffs.length === 7, 'Checking for the correct number of differences');
  105 +
  106 + var eqCount = 0;
  107 + var missingCount = 0;
  108 +
  109 +
  110 + jdd.diffs.forEach(function (diff) {
  111 + if (diff.type === jdd.EQUALITY) {
  112 + eqCount++;
  113 + } else if (diff.type === jdd.MISSING) {
  114 + missingCount++;
  115 + }
  116 + });
  117 +
  118 + assert.ok(eqCount === 6, 'Checking unequal values');
  119 + assert.ok(missingCount === 1, 'Checking missing values');
  120 +
  121 + $('#textarealeft').val('');
  122 + $('#textarearight').val('');
  123 + jdd.setupNewDiff();
  124 +});
  125 +
  126 +/**
  127 + * This test works a lot like the array to object compare tests, but it switches the left and right
  128 + * values. We've had a few bugs that only show up when the difference is on a specific side.
  129 + */
  130 +QUnit.test('Object to array compare tests', function (assert) {
  131 + $('#textarealeft').val('{"foo":[{ "OBJ_ID": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "b3067a77-875b-4208-9ee3-39128adeb654", "lastLogon": "0", "sAMAccountName": "ksmith", "userPrincipalName": "ksmith@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Kate Smith,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"},{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]}');
  132 + $('#textarearight').val('[{ "OBJ_ID": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com", "userAccountControl": "512", "objectGUID": "c3f7dae9-9b4f-4d55-a1ec-bf9ef45061c3", "lastLogon": "130766915788304915", "sAMAccountName": "tswan", "userPrincipalName": "tswan@cloudaddc.qalab.cam.novell.com", "distinguishedName": "CN=Timothy Swan,OU=Users,OU=Willow,DC=cloudaddc,DC=qalab,DC=cam,DC=novell,DC=com"}]');
  133 +
  134 + jdd.compare();
  135 +
  136 + // This test makes sure there wasn't a parsing error
  137 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  138 +
  139 + assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
  140 +
  141 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
  142 +
  143 + $('#textarealeft').val('');
  144 + $('#textarearight').val('');
  145 + jdd.setupNewDiff();
  146 +});
  147 +
  148 +/**
  149 + * These tests cover differences with whitespace in them. Specifically cases where there are new lines, backspaces,
  150 + * tabs, and other whitespace characters within the JSON keys and values. These values are escaped in the JSON source,
  151 + * but show up unescaped in the JSON object. We need to make sure we add them back and convert the line numbers properly.
  152 + */
  153 +QUnit.test('Whitespace formatting tests', function (assert) {
  154 + $('#textarealeft').val('{"newline": "a\\nb","slash": "a\\\\b","quotes": "a\\"b","backspace": "a\\bb","formfeed": "a\\fb","carriagereturn": "a\\rb","tab": "a\\tb","a\\nb": "newline","a\\\\b": "slash","a\\"b": "quotes","a\\bb": "backspace","a\\fb": "formfeed","a\\rb": "carriagereturn","a\\tb": "tab"}');
  155 + $('#textarearight').val('{"newline": "a\\nbx","slash": "a\\\\bx","quotes": "a\\"bx","backspace": "a\\bbx","formfeed": "a\\fbx","carriagereturn": "a\\rbx","tab": "a\\tbx","a\\nb": "newline","a\\\\bx": "slash","a\\"bx": "quotes","a\\bbx": "backspace","a\\fbx": "formfeed","a\\rbx": "carriagereturn","a\\tbx": "tab"}');
  156 +
  157 + jdd.compare();
  158 +
  159 + // This test makes sure there wasn't a parsing error
  160 + assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
  161 +
  162 + assert.ok(jdd.diffs.length === 19, 'Checking for the correct number of differences');
  163 +
  164 + assert.ok(jdd.diffs[0].type === jdd.MISSING, 'Checking missing property');
  165 + assert.ok(jdd.diffs[0].msg === 'Missing property <code>a\\bx</code> from the object on the left side', 'Checking property formatting');
  166 +
  167 + assert.ok(jdd.diffs[9].type === jdd.MISSING, 'Checking missing property');
  168 + assert.ok(jdd.diffs[9].msg === 'Missing property <code>a\"b</code> from the object on the right side', 'Checking property formatting');
  169 +
  170 + assert.ok(jdd.diffs[14].type === jdd.EQUALITY, 'Checking missing property');
  171 +
  172 + assert.ok(jdd.diffs[18].type === jdd.EQUALITY, 'Checking missing property');
  173 +
  174 + $('#textarealeft').val('');
  175 + $('#textarearight').val('');
  176 + jdd.setupNewDiff();
  177 +});
  178 +
  179 +/**
  180 + * This test tests for null values when compared to arrays on the right side of the compare.
  181 + */
  182 +QUnit.test('Null array length tests - right side', function (assert) {
  183 + $('#textarealeft').val('{ "akey": [] }');
  184 + $('#textarearight').val('{ "akey": null }');
  185 +
  186 + jdd.compare();
  187 +
  188 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  189 +
  190 + $('#textarealeft').val('');
  191 + $('#textarearight').val('');
  192 + jdd.setupNewDiff();
  193 +});
  194 +
  195 +/**
  196 + * This test tests for null values when compared to arrays on the left side of the compare.
  197 + */
  198 +QUnit.test('Null array length tests - left side', function (assert) {
  199 + $('#textarealeft').val('{ "akey": null }');
  200 + $('#textarearight').val('{ "akey": [] }');
  201 +
  202 + jdd.compare();
  203 +
  204 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  205 +
  206 + $('#textarealeft').val('');
  207 + $('#textarearight').val('');
  208 + jdd.setupNewDiff();
  209 +});
  210 +
  211 +
  212 +/**
  213 + * This test tests for null values when compared to arrays on the right side of the compare.
  214 + */
  215 +QUnit.test('Null object length tests - right side', function (assert) {
  216 + $('#textarealeft').val('{ "akey": {} }');
  217 + $('#textarearight').val('{ "akey": null }');
  218 +
  219 + jdd.compare();
  220 +
  221 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  222 +
  223 + $('#textarealeft').val('');
  224 + $('#textarearight').val('');
  225 + jdd.setupNewDiff();
  226 +});
  227 +
  228 +/**
  229 + * This test tests for null values when compared to arrays on the left side of the compare.
  230 + */
  231 +QUnit.test('Null object length tests - left side', function (assert) {
  232 + $('#textarealeft').val('{ "akey": null }');
  233 + $('#textarearight').val('{ "akey": {} }');
  234 +
  235 + jdd.compare();
  236 +
  237 + assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
  238 +
  239 + $('#textarealeft').val('');
  240 + $('#textarearight').val('');
  241 + jdd.setupNewDiff();
  242 +});
  243 +
  244 +QUnit.test('Escaped quotes test', function (assert) {
  245 + $('#textarealeft').val('{"link": "<a href=\\\"http://google.com/\\\">Google</a>"}');
  246 + $('#textarearight').val('{"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"}');
  247 +
  248 + jdd.compare();
  249 + assert.ok(jdd.diffs[0].type === jdd.EQUALITY, 'Checking correct type');
  250 + assert.ok($('pre.right div.line2 span').text().trim() === '"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"', 'Checking escaped quotes');
  251 + assert.ok($('pre.left div.line2 span').text().trim() === '"link": "<a href=\\\"http://google.com/\\\">Google</a>"', 'Checking escaped quotes');
  252 +
  253 + $('#textarealeft').val('');
  254 + $('#textarearight').val('');
  255 + jdd.setupNewDiff();
  256 +});
  257 +
  258 +
  259 +QUnit.done(function () {
  260 + $('div.initContainer').hide();
  261 + $('div.diffContainer').hide();
  262 +});
  1 +/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
  2 +var jsl = typeof jsl === 'undefined' ? {} : jsl;
  3 +
  4 +/**
  5 + * jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability).
  6 + *
  7 +**/
  8 +jsl.format = (function () {
  9 +
  10 + function repeat(s, count) {
  11 + return new Array(count + 1).join(s);
  12 + }
  13 +
  14 + function formatJson(json) {
  15 + var i = 0,
  16 + il = 0,
  17 + tab = " ",
  18 + newJson = "",
  19 + indentLevel = 0,
  20 + inString = false,
  21 + currentChar = null;
  22 +
  23 + for (i = 0, il = json.length; i < il; i += 1) {
  24 + currentChar = json.charAt(i);
  25 +
  26 + switch (currentChar) {
  27 + case '{':
  28 + case '[':
  29 + if (!inString) {
  30 + newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
  31 + indentLevel += 1;
  32 + } else {
  33 + newJson += currentChar;
  34 + }
  35 + break;
  36 + case '}':
  37 + case ']':
  38 + if (!inString) {
  39 + indentLevel -= 1;
  40 + newJson += "\n" + repeat(tab, indentLevel) + currentChar;
  41 + } else {
  42 + newJson += currentChar;
  43 + }
  44 + break;
  45 + case ',':
  46 + if (!inString) {
  47 + newJson += ",\n" + repeat(tab, indentLevel);
  48 + } else {
  49 + newJson += currentChar;
  50 + }
  51 + break;
  52 + case ':':
  53 + if (!inString) {
  54 + newJson += ": ";
  55 + } else {
  56 + newJson += currentChar;
  57 + }
  58 + break;
  59 + case ' ':
  60 + case "\n":
  61 + case "\t":
  62 + if (inString) {
  63 + newJson += currentChar;
  64 + }
  65 + break;
  66 + case '"':
  67 + if (i > 0 && json.charAt(i - 1) !== '\\') {
  68 + inString = !inString;
  69 + }
  70 + newJson += currentChar;
  71 + break;
  72 + default:
  73 + newJson += currentChar;
  74 + break;
  75 + }
  76 + }
  77 +
  78 + return newJson;
  79 + }
  80 +
  81 + return { "formatJson": formatJson };
  82 +
  83 +}());
  1 +/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
  2 +var jsl = typeof jsl === 'undefined' ? {} : jsl;
  3 +
  4 +/**
  5 + * Helper Function for Caret positioning
  6 + * Gratefully borrowed from the Masked Input Plugin by Josh Bush
  7 + * http://digitalbush.com/projects/masked-input-plugin
  8 +**/
  9 +$.fn.caret = function (begin, end) {
  10 + if (this.length === 0) {
  11 + return;
  12 + }
  13 + if (typeof begin === 'number') {
  14 + end = (typeof end === 'number') ? end : begin;
  15 + return this.each(function () {
  16 + if (this.setSelectionRange) {
  17 + this.focus();
  18 + this.setSelectionRange(begin, end);
  19 + } else if (this.createTextRange) {
  20 + var range = this.createTextRange();
  21 + range.collapse(true);
  22 + range.moveEnd('character', end);
  23 + range.moveStart('character', begin);
  24 + range.select();
  25 + }
  26 + });
  27 + } else {
  28 + if (this[0].setSelectionRange) {
  29 + begin = this[0].selectionStart;
  30 + end = this[0].selectionEnd;
  31 + } else if (document.selection && document.selection.createRange) {
  32 + var range = document.selection.createRange();
  33 + begin = -range.duplicate().moveStart('character', -100000);
  34 + end = begin + range.text.length;
  35 + }
  36 + return {"begin": begin, "end": end};
  37 + }
  38 +};
  39 +
  40 +
  41 +/**
  42 + * jsl.interactions - provides support for interactions within JSON Lint.
  43 + *
  44 +**/
  45 +jsl.interactions = (function () {
  46 + var reformatParam,
  47 + reformat,
  48 + compress;
  49 +
  50 +
  51 + /******* UTILITY METHODS *******/
  52 +
  53 + /**
  54 + * Get the Nth position of a character in a string
  55 + * @searchStr the string to search through
  56 + * @char the character to find
  57 + * @pos int the nth character to find, 1 based.
  58 + *
  59 + * @return int the position of the character found
  60 + **/
  61 + function getNthPos(searchStr, char, pos) {
  62 + var i,
  63 + charCount = 0,
  64 + strArr = searchStr.split(char);
  65 +
  66 + if (pos === 0) {
  67 + return 0;
  68 + }
  69 +
  70 + for (i = 0; i < pos; i++) {
  71 + if (i >= strArr.length) {
  72 + return -1;
  73 + }
  74 +
  75 + // +1 because we split out some characters
  76 + charCount += strArr[i].length + char.length;
  77 + }
  78 +
  79 + return charCount;
  80 + }
  81 +
  82 + /**
  83 + * Get a URL parameter from the current windows URL.
  84 + * Courtesy Paul Oppenheim: http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
  85 + * @param name the parameter to retrieve
  86 + * @return string the url parameter's value, if any
  87 + **/
  88 + function getURLParameter(name) {
  89 + param = (new RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || ['', null])[1];
  90 + if (param) {
  91 + return decodeURIComponent(param);
  92 + } else {
  93 + return null;
  94 + }
  95 + }
  96 +
  97 + /******* INTERACTION METHODS *******/
  98 +
  99 + /**
  100 + * Validate the JSON we've been given, displaying an error or success message.
  101 + * @return void
  102 + **/
  103 + function validate() {
  104 + var lineNum,
  105 + lineMatches,
  106 + lineStart,
  107 + lineEnd,
  108 + jsonVal,
  109 + result;
  110 +
  111 + jsonVal = $('#json_input').val();
  112 +
  113 + try {
  114 + result = jsl.parser.parse(jsonVal);
  115 +
  116 + if (result) {
  117 + $('#results').removeClass('error').addClass('success');
  118 + $('div.linedwrap').removeClass('redBorder').addClass('greenBorder');
  119 + $('#results').text('Valid JSON');
  120 +
  121 + if (reformat) {
  122 + $('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, " "));
  123 + }
  124 +
  125 + if (compress) {
  126 + $('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, ""));
  127 + }
  128 + } else {
  129 + alert("An unknown error occurred. Please contact Arc90.");
  130 + }
  131 + } catch (parseException) {
  132 +
  133 + /**
  134 + * If we failed to validate, run our manual formatter and then re-validate so that we
  135 + * can get a better line number. On a successful validate, we don't want to run our
  136 + * manual formatter because the automatic one is faster and probably more reliable.
  137 + **/
  138 + try {
  139 + if (reformat) {
  140 + jsonVal = jsl.format.formatJson($('#json_input').val());
  141 + $('#json_input').val(jsonVal);
  142 + result = jsl.parser.parse($('#json_input').val());
  143 + }
  144 + } catch(e) {
  145 + parseException = e;
  146 + }
  147 +
  148 + lineMatches = parseException.message.match(/line ([0-9]*)/);
  149 + if (lineMatches && typeof lineMatches === "object" && lineMatches.length > 1) {
  150 + lineNum = parseInt(lineMatches[1], 10);
  151 +
  152 + if (lineNum === 1) {
  153 + lineStart = 0;
  154 + } else {
  155 + lineStart = getNthPos(jsonVal, "\n", lineNum - 1);
  156 + }
  157 +
  158 + lineEnd = jsonVal.indexOf("\n", lineStart);
  159 + if (lineEnd < 0) {
  160 + lineEnd = jsonVal.length;
  161 + }
  162 +
  163 + $('#json_input').focus().caret(lineStart, lineEnd);
  164 + }
  165 +
  166 + $('#results').text(parseException.message);
  167 +
  168 + $('#results').removeClass('success').addClass('error');
  169 + $('div.linedwrap').removeClass('greenBorder').addClass('redBorder');
  170 + }
  171 +
  172 + $('#loadSpinner').hide();
  173 + }
  174 +
  175 + /**
  176 + * Initialize variables, add event listeners, etc.
  177 + *
  178 + * @return void
  179 + **/
  180 + function init() {
  181 + reformatParam = getURLParameter('reformat');
  182 + reformat = reformatParam !== '0' && reformatParam !== 'no';
  183 + compress = reformatParam === 'compress',
  184 + jsonParam = getURLParameter('json');
  185 +
  186 + if (compress) {
  187 + $('#headerText').html('JSONLint<span class="light">Compressor</span>');
  188 + }
  189 +
  190 + if (!reformat) {
  191 + $('#headerText').html('JSONLint<span class="light">Lite</span>');
  192 + }
  193 +
  194 + $('#validate').click(function () {
  195 + $('#results_header, #loadSpinner').show();
  196 +
  197 + var jsonVal = $.trim($('#json_input').val());
  198 +
  199 + if (jsonVal.substring(0, 4).toLowerCase() === "http") {
  200 + $.post("proxy.php", {"url": jsonVal}, function (responseObj) {
  201 + $('#json_input').val(responseObj.content);
  202 + validate();
  203 + }, 'json');
  204 + } else {
  205 + validate();
  206 + }
  207 +
  208 + return false;
  209 + });
  210 +
  211 + $('#json_input').keyup(function () {
  212 + $('div.linedwrap').removeClass('greenBorder').removeClass('redBorder');
  213 + }).linedtextarea({
  214 + selectedClass: 'lineselect'
  215 + }).focus();
  216 +
  217 + $('#reset').click(function () {
  218 + $('#json_input').val('').focus();
  219 + });
  220 +
  221 + $('#faqButton').click(function () {
  222 + $('#faq').slideToggle();
  223 + });
  224 +
  225 + if (jsonParam) {
  226 + $('#json_input').val(jsonParam);
  227 + $('#validate').click();
  228 + }
  229 + }
  230 +
  231 + return {
  232 + 'init': init
  233 + };
  234 +}());
  235 +
  236 +$(function () {
  237 + jsl.interactions.init();
  238 +});
  1 +var jsl = typeof(jsl) === 'undefined' ? {} : jsl;
  2 +/**
  3 + * JSON Lint Parser gratefully provided by Zach Carter
  4 + * https://github.com/zaach/jsonlint
  5 +**/
  6 +jsl.parser = function(){var a=!0,b=!1,c={},d=function(){var a={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONObject:13,EOF:14,JSONArray:15,JSONValue:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[12,2],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[13,2],[13,3],[20,3],[19,1],[19,3],[15,2],[15,3],[25,1],[25,3]],performAction:function(a,b,c,d,e,f,g){var h=f.length-1;switch(e){case 1:this.$=a;break;case 2:this.$=Number(a);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=f[h-1];case 7:return this.$=f[h-1];case 8:this.$=f[h];break;case 9:this.$=f[h];break;case 10:this.$=f[h];break;case 11:this.$=f[h];break;case 12:this.$=f[h];break;case 13:this.$=f[h];break;case 14:this.$={};break;case 15:this.$=f[h-1];break;case 16:this.$=[f[h-2],f[h]];break;case 17:this.$={},this.$[f[h][0]]=f[h][1];break;case 18:this.$=f[h-2],f[h-2][f[h][0]]=f[h][1];break;case 19:this.$=[];break;case 20:this.$=f[h-1];break;case 21:this.$=[f[h]];break;case 22:this.$=f[h-2],f[h-2].push(f[h])}},table:[{12:1,13:2,15:3,17:[1,4],23:[1,5]},{1:[3]},{14:[1,6]},{14:[1,7]},{3:11,4:[1,12],18:[1,8],19:9,20:10},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:15,17:[1,4],23:[1,5],24:[1,13],25:14},{1:[2,6]},{1:[2,7]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{18:[1,26],22:[1,27]},{18:[2,17],22:[2,17]},{21:[1,28]},{18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{22:[1,30],24:[1,29]},{22:[2,21],24:[2,21]},{18:[2,8],22:[2,8],24:[2,8]},{18:[2,9],22:[2,9],24:[2,9]},{18:[2,10],22:[2,10],24:[2,10]},{18:[2,11],22:[2,11],24:[2,11]},{18:[2,12],22:[2,12],24:[2,12]},{18:[2,13],22:[2,13],24:[2,13]},{18:[2,3],22:[2,3],24:[2,3]},{18:[2,4],22:[2,4],24:[2,4]},{18:[2,5],22:[2,5],24:[2,5]},{18:[2,2],22:[2,2],24:[2,2]},{14:[2,15],18:[2,15],22:[2,15],24:[2,15]},{3:11,4:[1,12],20:31},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:32,17:[1,4],23:[1,5]},{14:[2,20],18:[2,20],22:[2,20],24:[2,20]},{3:18,4:[1,12],5:19,6:[1,25],7:16,8:[1,22],9:17,10:[1,23],11:[1,24],13:20,15:21,16:33,17:[1,4],23:[1,5]},{18:[2,18],22:[2,18]},{18:[2,16],22:[2,16]},{22:[2,22],24:[2,22]}],defaultActions:{6:[2,6],7:[2,7]},parseError:function(a,b){throw new Error(a)},parse:function(a){function o(){var a;a=b.lexer.lex()||1,typeof a!="number"&&(a=b.symbols_[a]||a);return a}function n(a){c.length=c.length-2*a,d.length=d.length-a,e.length=e.length-a}var b=this,c=[0],d=[null],e=[],f=this.table,g="",h=0,i=0,j=0,k=2,l=1;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var m=this.lexer.yylloc;e.push(m),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var p,q,r,s,t,u,v={},w,x,y,z;for(;;){r=c[c.length-1],this.defaultActions[r]?s=this.defaultActions[r]:(p==null&&(p=o()),s=f[r]&&f[r][p]);if(typeof s=="undefined"||!s.length||!s[0]){if(!j){z=[];for(w in f[r])this.terminals_[w]&&w>2&&z.push("'"+this.terminals_[w]+"'");var A="";this.lexer.showPosition?A="Parse error on line "+(h+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+z.join(", "):A="Parse error on line "+(h+1)+": Unexpected "+(p==1?"end of input":"'"+(this.terminals_[p]||p)+"'"),this.parseError(A,{text:this.lexer.match,token:this.terminals_[p]||p,line:this.lexer.yylineno,loc:m,expected:z})}if(j==3){if(p==l)throw new Error(A||"Parsing halted.");i=this.lexer.yyleng,g=this.lexer.yytext,h=this.lexer.yylineno,m=this.lexer.yylloc,p=o()}for(;;){if(k.toString()in f[r])break;if(r==0)throw new Error(A||"Parsing halted.");n(1),r=c[c.length-1]}q=p,p=k,r=c[c.length-1],s=f[r]&&f[r][k],j=3}if(s[0]instanceof Array&&s.length>1)throw new Error("Parse Error: multiple actions possible at state: "+r+", token: "+p);switch(s[0]){case 1:c.push(p),d.push(this.lexer.yytext),e.push(this.lexer.yylloc),c.push(s[1]),p=null,q?(p=q,q=null):(i=this.lexer.yyleng,g=this.lexer.yytext,h=this.lexer.yylineno,m=this.lexer.yylloc,j>0&&j--);break;case 2:x=this.productions_[s[1]][1],v.$=d[d.length-x],v._$={first_line:e[e.length-(x||1)].first_line,last_line:e[e.length-1].last_line,first_column:e[e.length-(x||1)].first_column,last_column:e[e.length-1].last_column},u=this.performAction.call(v,g,i,h,this.yy,s[1],d,e);if(typeof u!="undefined")return u;x&&(c=c.slice(0,-1*x*2),d=d.slice(0,-1*x),e=e.slice(0,-1*x)),c.push(this.productions_[s[1]][0]),d.push(v.$),e.push(v._$),y=f[c[c.length-2]][c[c.length-1]],c.push(y);break;case 3:return!0}}return!0}},f=function(){var a={EOF:1,parseError:function(a,b){if(this.yy.parseError)this.yy.parseError(a,b);else throw new Error(a)},setInput:function(a){this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0};return this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.match+=a,this.matched+=a;var b=a.match(/\n/);b&&this.yylineno++,this._input=this._input.slice(1);return a},unput:function(a){this._input=a+this._input;return this},more:function(){this._more=!0;return this},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;a.length<20&&(a+=this._input.substr(0,20-a.length));return(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d;this._more||(this.yytext="",this.match="");var e=this._currentRules();for(var f=0;f<e.length;f++){b=this._input.match(this.rules[e[f]]);if(b){d=b[0].match(/\n.*/g),d&&(this.yylineno+=d.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:d?d[d.length-1].length-1:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,e[f],this.conditionStack[this.conditionStack.length-1]);if(a)return a;return}}if(this._input==="")return this.EOF;this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return typeof a!="undefined"?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules}};a.performAction=function(a,b,c,d){var e=d;switch(c){case 0:break;case 1:return 6;case 2:b.yytext=b.yytext.substr(1,b.yyleng-2);return 4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},a.rules=[/^\s+/,/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][-+]?[0-9]+)?\b/,/^"(\\["bfnrt/\\]|\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f"\\])*"/,/^\{/,/^\}/,/^\[/,/^\]/,/^,/,/^:/,/^true\b/,/^false\b/,/^null\b/,/^$/,/^./],a.conditions={INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}};return a}();a.lexer=f;return a}();typeof a!="undefined"&&typeof c!="undefined"&&(c.parser=d,c.parse=function(){return d.parse.apply(d,arguments)},c.main=function(b){if(!b[1])throw new Error("Usage: "+b[0]+" FILE");if(typeof process!="undefined")var d=a("fs").readFileSync(a("path").join(process.cwd(),b[1]),"utf8");else var e=a("file").path(a("file").cwd()),d=e.join(b[1]).read({charset:"utf-8"});return c.parser.parse(d)},typeof b!="undefined"&&a.main===b&&c.main(typeof process!="undefined"?process.argv.slice(1):a("system").args));return c}()
  1 +{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"]}
  1 +<?php
  2 +
  3 +$url = filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
  4 +
  5 +if (!$url || !preg_match("/^https?:/i", $url)) {
  6 + echo '{ "result": "Invalid URL. Please check your URL and try again.", "error": true }';
  7 + return;
  8 +}
  9 +
  10 +$parsed_url = parse_url($url);
  11 +$scheme = strtolower($parsed_url['scheme']);
  12 +if (($scheme !== "http" && $scheme !== "https")) {
  13 + echo '{ "result": "Invalid URL - Please check your URL and try again.", "error": true }';
  14 + return;
  15 +}
  16 +
  17 +$ch = curl_init($url);
  18 +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  19 +curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
  20 +curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  21 +$data = curl_exec($ch);
  22 +$info = curl_getinfo($ch);
  23 +curl_close($ch);
  24 +
  25 +if ($data === false) {
  26 + echo '{ "result": "Unable to parse URL. Please check your URL and try again.", "error": true }';
  27 + return;
  28 +}
  29 +
  30 +$contentLength = intval($info['download_content_length']);
  31 +$status = intval($info['http_code']);
  32 +
  33 +if ($status >= 400) {
  34 + echo '{ "result": "URL returned bad status code ' . $status . '.", "error": true }';
  35 + return;
  36 +}
  37 +
  38 +if ($contentLength >= 52428800) {
  39 + echo '{ "result": "URL content length greater than 10 megs (' . $contentLength . '). Validation not available for files this large.", "responseCode": "1" }';
  40 + return;
  41 +}
  42 +
  43 +$response = new StdClass();
  44 +$response->status = $status;
  45 +$response->length = $contentLength;
  46 +$response->url = $info['url'];
  47 +$response->content = $data;
  48 +
  49 +echo json_encode($response);
  50 +
  51 +?>
  1 +/*!
  2 + * QUnit 2.1.1
  3 + * https://qunitjs.com/
  4 + *
  5 + * Copyright jQuery Foundation and other contributors
  6 + * Released under the MIT license
  7 + * https://jquery.org/license
  8 + *
  9 + * Date: 2017-01-06T01:52Z
  10 + */
  11 +
  12 +/** Font Family and Sizes */
  13 +
  14 +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
  15 + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
  16 +}
  17 +
  18 +#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
  19 +#qunit-tests { font-size: smaller; }
  20 +
  21 +
  22 +/** Resets */
  23 +
  24 +#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
  25 + margin: 0;
  26 + padding: 0;
  27 +}
  28 +
  29 +
  30 +/** Header (excluding toolbar) */
  31 +
  32 +#qunit-header {
  33 + padding: 0.5em 0 0.5em 1em;
  34 +
  35 + color: #8699A4;
  36 + background-color: #0D3349;
  37 +
  38 + font-size: 1.5em;
  39 + line-height: 1em;
  40 + font-weight: 400;
  41 +
  42 + border-radius: 5px 5px 0 0;
  43 +}
  44 +
  45 +#qunit-header a {
  46 + text-decoration: none;
  47 + color: #C2CCD1;
  48 +}
  49 +
  50 +#qunit-header a:hover,
  51 +#qunit-header a:focus {
  52 + color: #FFF;
  53 +}
  54 +
  55 +#qunit-banner {
  56 + height: 5px;
  57 +}
  58 +
  59 +#qunit-filteredTest {
  60 + padding: 0.5em 1em 0.5em 1em;
  61 + color: #366097;
  62 + background-color: #F4FF77;
  63 +}
  64 +
  65 +#qunit-userAgent {
  66 + padding: 0.5em 1em 0.5em 1em;
  67 + color: #FFF;
  68 + background-color: #2B81AF;
  69 + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
  70 +}
  71 +
  72 +
  73 +/** Toolbar */
  74 +
  75 +#qunit-testrunner-toolbar {
  76 + padding: 0.5em 1em 0.5em 1em;
  77 + color: #5E740B;
  78 + background-color: #EEE;
  79 +}
  80 +
  81 +#qunit-testrunner-toolbar .clearfix {
  82 + height: 0;
  83 + clear: both;
  84 +}
  85 +
  86 +#qunit-testrunner-toolbar label {
  87 + display: inline-block;
  88 +}
  89 +
  90 +#qunit-testrunner-toolbar input[type=checkbox],
  91 +#qunit-testrunner-toolbar input[type=radio] {
  92 + margin: 3px;
  93 + vertical-align: -2px;
  94 +}
  95 +
  96 +#qunit-testrunner-toolbar input[type=text] {
  97 + box-sizing: border-box;
  98 + height: 1.6em;
  99 +}
  100 +
  101 +.qunit-url-config,
  102 +.qunit-filter,
  103 +#qunit-modulefilter {
  104 + display: inline-block;
  105 + line-height: 2.1em;
  106 +}
  107 +
  108 +.qunit-filter,
  109 +#qunit-modulefilter {
  110 + float: right;
  111 + position: relative;
  112 + margin-left: 1em;
  113 +}
  114 +
  115 +.qunit-url-config label {
  116 + margin-right: 0.5em;
  117 +}
  118 +
  119 +#qunit-modulefilter-search {
  120 + box-sizing: border-box;
  121 + width: 400px;
  122 +}
  123 +
  124 +#qunit-modulefilter-search-container:after {
  125 + position: absolute;
  126 + right: 0.3em;
  127 + content: "\25bc";
  128 + color: black;
  129 +}
  130 +
  131 +#qunit-modulefilter-dropdown {
  132 + /* align with #qunit-modulefilter-search */
  133 + box-sizing: border-box;
  134 + width: 400px;
  135 + position: absolute;
  136 + right: 0;
  137 + top: 50%;
  138 + margin-top: 0.8em;
  139 +
  140 + border: 1px solid #D3D3D3;
  141 + border-top: none;
  142 + border-radius: 0 0 .25em .25em;
  143 + color: #000;
  144 + background-color: #F5F5F5;
  145 + z-index: 99;
  146 +}
  147 +
  148 +#qunit-modulefilter-dropdown a {
  149 + color: inherit;
  150 + text-decoration: none;
  151 +}
  152 +
  153 +#qunit-modulefilter-dropdown .clickable.checked {
  154 + font-weight: bold;
  155 + color: #000;
  156 + background-color: #D2E0E6;
  157 +}
  158 +
  159 +#qunit-modulefilter-dropdown .clickable:hover {
  160 + color: #FFF;
  161 + background-color: #0D3349;
  162 +}
  163 +
  164 +#qunit-modulefilter-actions {
  165 + display: block;
  166 + overflow: auto;
  167 +
  168 + /* align with #qunit-modulefilter-dropdown-list */
  169 + font: smaller/1.5em sans-serif;
  170 +}
  171 +
  172 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
  173 + box-sizing: border-box;
  174 + max-height: 2.8em;
  175 + display: block;
  176 + padding: 0.4em;
  177 +}
  178 +
  179 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
  180 + float: right;
  181 + font: inherit;
  182 +}
  183 +
  184 +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
  185 + /* insert padding to align with checkbox margins */
  186 + padding-left: 3px;
  187 +}
  188 +
  189 +#qunit-modulefilter-dropdown-list {
  190 + max-height: 200px;
  191 + overflow-y: auto;
  192 + margin: 0;
  193 + border-top: 2px groove threedhighlight;
  194 + padding: 0.4em 0 0;
  195 + font: smaller/1.5em sans-serif;
  196 +}
  197 +
  198 +#qunit-modulefilter-dropdown-list li {
  199 + white-space: nowrap;
  200 + overflow: hidden;
  201 + text-overflow: ellipsis;
  202 +}
  203 +
  204 +#qunit-modulefilter-dropdown-list .clickable {
  205 + display: block;
  206 + padding-left: 0.15em;
  207 +}
  208 +
  209 +
  210 +/** Tests: Pass/Fail */
  211 +
  212 +#qunit-tests {
  213 + list-style-position: inside;
  214 +}
  215 +
  216 +#qunit-tests li {
  217 + padding: 0.4em 1em 0.4em 1em;
  218 + border-bottom: 1px solid #FFF;
  219 + list-style-position: inside;
  220 +}
  221 +
  222 +#qunit-tests > li {
  223 + display: none;
  224 +}
  225 +
  226 +#qunit-tests li.running,
  227 +#qunit-tests li.pass,
  228 +#qunit-tests li.fail,
  229 +#qunit-tests li.skipped,
  230 +#qunit-tests li.aborted {
  231 + display: list-item;
  232 +}
  233 +
  234 +#qunit-tests.hidepass {
  235 + position: relative;
  236 +}
  237 +
  238 +#qunit-tests.hidepass li.running,
  239 +#qunit-tests.hidepass li.pass {
  240 + visibility: hidden;
  241 + position: absolute;
  242 + width: 0;
  243 + height: 0;
  244 + padding: 0;
  245 + border: 0;
  246 + margin: 0;
  247 +}
  248 +
  249 +#qunit-tests li strong {
  250 + cursor: pointer;
  251 +}
  252 +
  253 +#qunit-tests li.skipped strong {
  254 + cursor: default;
  255 +}
  256 +
  257 +#qunit-tests li a {
  258 + padding: 0.5em;
  259 + color: #C2CCD1;
  260 + text-decoration: none;
  261 +}
  262 +
  263 +#qunit-tests li p a {
  264 + padding: 0.25em;
  265 + color: #6B6464;
  266 +}
  267 +#qunit-tests li a:hover,
  268 +#qunit-tests li a:focus {
  269 + color: #000;
  270 +}
  271 +
  272 +#qunit-tests li .runtime {
  273 + float: right;
  274 + font-size: smaller;
  275 +}
  276 +
  277 +.qunit-assert-list {
  278 + margin-top: 0.5em;
  279 + padding: 0.5em;
  280 +
  281 + background-color: #FFF;
  282 +
  283 + border-radius: 5px;
  284 +}
  285 +
  286 +.qunit-source {
  287 + margin: 0.6em 0 0.3em;
  288 +}
  289 +
  290 +.qunit-collapsed {
  291 + display: none;
  292 +}
  293 +
  294 +#qunit-tests table {
  295 + border-collapse: collapse;
  296 + margin-top: 0.2em;
  297 +}
  298 +
  299 +#qunit-tests th {
  300 + text-align: right;
  301 + vertical-align: top;
  302 + padding: 0 0.5em 0 0;
  303 +}
  304 +
  305 +#qunit-tests td {
  306 + vertical-align: top;
  307 +}
  308 +
  309 +#qunit-tests pre {
  310 + margin: 0;
  311 + white-space: pre-wrap;
  312 + word-wrap: break-word;
  313 +}
  314 +
  315 +#qunit-tests del {
  316 + color: #374E0C;
  317 + background-color: #E0F2BE;
  318 + text-decoration: none;
  319 +}
  320 +
  321 +#qunit-tests ins {
  322 + color: #500;
  323 + background-color: #FFCACA;
  324 + text-decoration: none;
  325 +}
  326 +
  327 +/*** Test Counts */
  328 +
  329 +#qunit-tests b.counts { color: #000; }
  330 +#qunit-tests b.passed { color: #5E740B; }
  331 +#qunit-tests b.failed { color: #710909; }
  332 +
  333 +#qunit-tests li li {
  334 + padding: 5px;
  335 + background-color: #FFF;
  336 + border-bottom: none;
  337 + list-style-position: inside;
  338 +}
  339 +
  340 +/*** Passing Styles */
  341 +
  342 +#qunit-tests li li.pass {
  343 + color: #3C510C;
  344 + background-color: #FFF;
  345 + border-left: 10px solid #C6E746;
  346 +}
  347 +
  348 +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
  349 +#qunit-tests .pass .test-name { color: #366097; }
  350 +
  351 +#qunit-tests .pass .test-actual,
  352 +#qunit-tests .pass .test-expected { color: #999; }
  353 +
  354 +#qunit-banner.qunit-pass { background-color: #C6E746; }
  355 +
  356 +/*** Failing Styles */
  357 +
  358 +#qunit-tests li li.fail {
  359 + color: #710909;
  360 + background-color: #FFF;
  361 + border-left: 10px solid #EE5757;
  362 + white-space: pre;
  363 +}
  364 +
  365 +#qunit-tests > li:last-child {
  366 + border-radius: 0 0 5px 5px;
  367 +}
  368 +
  369 +#qunit-tests .fail { color: #000; background-color: #EE5757; }
  370 +#qunit-tests .fail .test-name,
  371 +#qunit-tests .fail .module-name { color: #000; }
  372 +
  373 +#qunit-tests .fail .test-actual { color: #EE5757; }
  374 +#qunit-tests .fail .test-expected { color: #008000; }
  375 +
  376 +#qunit-banner.qunit-fail { background-color: #EE5757; }
  377 +
  378 +
  379 +/*** Aborted tests */
  380 +#qunit-tests .aborted { color: #000; background-color: orange; }
  381 +/*** Skipped tests */
  382 +
  383 +#qunit-tests .skipped {
  384 + background-color: #EBECE9;
  385 +}
  386 +
  387 +#qunit-tests .qunit-skipped-label {
  388 + background-color: #F4FF77;
  389 + display: inline-block;
  390 + font-style: normal;
  391 + color: #366097;
  392 + line-height: 1.8em;
  393 + padding: 0 0.5em;
  394 + margin: -0.4em 0.4em -0.4em 0;
  395 +}
  396 +
  397 +/** Result */
  398 +
  399 +#qunit-testresult {
  400 + color: #2B81AF;
  401 + background-color: #D2E0E6;
  402 +
  403 + border-bottom: 1px solid #FFF;
  404 +}
  405 +#qunit-testresult .clearfix {
  406 + height: 0;
  407 + clear: both;
  408 +}
  409 +#qunit-testresult .module-name {
  410 + font-weight: 700;
  411 +}
  412 +#qunit-testresult-display {
  413 + padding: 0.5em 1em 0.5em 1em;
  414 + width: 85%;
  415 + float:left;
  416 +}
  417 +#qunit-testresult-controls {
  418 + padding: 0.5em 1em 0.5em 1em;
  419 + width: 10%;
  420 + float:left;
  421 +}
  422 +
  423 +/** Fixture */
  424 +
  425 +#qunit-fixture {
  426 + position: absolute;
  427 + top: -10000px;
  428 + left: -10000px;
  429 + width: 1000px;
  430 + height: 1000px;
  431 +}
This diff could not be displayed because it is too large.
  1 +/* http://meyerweb.com/eric/tools/css/reset/
  2 + v2.0 | 20110126
  3 + License: none (public domain)
  4 +*/
  5 +
  6 +html, body, div, span, applet, object, iframe,
  7 +h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  8 +a, abbr, acronym, address, big, cite, code,
  9 +del, dfn, em, img, ins, kbd, q, s, samp,
  10 +small, strike, strong, sub, sup, tt, var,
  11 +b, u, i, center,
  12 +dl, dt, dd, ol, ul, li,
  13 +fieldset, form, label, legend,
  14 +table, caption, tbody, tfoot, thead, tr, th, td,
  15 +article, aside, canvas, details, embed,
  16 +figure, figcaption, footer, header, hgroup,
  17 +menu, nav, output, ruby, section, summary,
  18 +time, mark, audio, video {
  19 + margin: 0;
  20 + padding: 0;
  21 + border: 0;
  22 + font: inherit;
  23 + vertical-align: baseline;
  24 +}
  25 +/* HTML5 display-role reset for older browsers */
  26 +article, aside, details, figcaption, figure,
  27 +footer, header, hgroup, menu, nav, section {
  28 + display: block;
  29 +}
  30 +body {
  31 + line-height: 1;
  32 +}
  33 +ol, ul {
  34 + list-style: none;
  35 +}
  36 +blockquote, q {
  37 + quotes: none;
  38 +}
  39 +blockquote:before, blockquote:after,
  40 +q:before, q:after {
  41 + content: '';
  42 + content: none;
  43 +}
  44 +table {
  45 + border-collapse: collapse;
  46 + border-spacing: 0;
  47 +}
  1 +@-moz-keyframes throbber-loader {
  2 + 0% {
  3 + background: #dde2e7;
  4 + }
  5 + 10% {
  6 + background: #6b9dc8;
  7 + }
  8 + 40% {
  9 + background: #dde2e7;
  10 + }
  11 +}
  12 +@-webkit-keyframes throbber-loader {
  13 + 0% {
  14 + background: #dde2e7;
  15 + }
  16 + 10% {
  17 + background: #6b9dc8;
  18 + }
  19 + 40% {
  20 + background: #dde2e7;
  21 + }
  22 +}
  23 +@keyframes throbber-loader {
  24 + 0% {
  25 + background: #dde2e7;
  26 + }
  27 + 10% {
  28 + background: #6b9dc8;
  29 + }
  30 + 40% {
  31 + background: #dde2e7;
  32 + }
  33 +}
  34 +/* :not(:required) hides these rules from IE9 and below */
  35 +.throbber-loader:not(:required) {
  36 + -moz-animation: throbber-loader 2000ms 300ms infinite ease-out;
  37 + -webkit-animation: throbber-loader 2000ms 300ms infinite ease-out;
  38 + animation: throbber-loader 2000ms 300ms infinite ease-out;
  39 + background: #dde2e7;
  40 + display: inline-block;
  41 + position: relative;
  42 + text-indent: -9999px;
  43 + width: 0.9em;
  44 + height: 1.5em;
  45 + margin: 0 1.6em;
  46 +}
  47 +.throbber-loader:not(:required):before, .throbber-loader:not(:required):after {
  48 + background: #dde2e7;
  49 + content: '\x200B';
  50 + display: inline-block;
  51 + width: 0.9em;
  52 + height: 1.5em;
  53 + position: absolute;
  54 + top: 0;
  55 +}
  56 +.throbber-loader:not(:required):before {
  57 + -moz-animation: throbber-loader 2000ms 150ms infinite ease-out;
  58 + -webkit-animation: throbber-loader 2000ms 150ms infinite ease-out;
  59 + animation: throbber-loader 2000ms 150ms infinite ease-out;
  60 + left: -1.6em;
  61 +}
  62 +.throbber-loader:not(:required):after {
  63 + -moz-animation: throbber-loader 2000ms 450ms infinite ease-out;
  64 + -webkit-animation: throbber-loader 2000ms 450ms infinite ease-out;
  65 + animation: throbber-loader 2000ms 450ms infinite ease-out;
  66 + right: -1.6em;
  67 +}
  1 +{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}