Showing
33 changed files
with
4633 additions
and
28 deletions
Too many changes to show.
To preserve performance only 33 of 33+ files are displayed.
| @@ -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' => '个人信息', |
application/admin/view/system/detail.html
0 → 100644
| 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"/} |
application/admin/view/system/differ.html
0 → 100644
| 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> |
public/static/jsondiff/.jshintrc
0 → 100644
| 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 | +} |
public/static/jsondiff/LICENSE.txt
0 → 100644
| 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 | + |
public/static/jsondiff/README.md
0 → 100644
| 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) |
public/static/jsondiff/favicon.ico
0 → 100644
No preview for this file type
public/static/jsondiff/index-test.html
0 → 100644
| 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> |
public/static/jsondiff/index.html
0 → 100644
| 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> |
public/static/jsondiff/jdd.css
0 → 100644
| 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 | +} |
public/static/jsondiff/jdd.js
0 → 100644
| 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"><</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">></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 | +} |
public/static/jsondiff/jdd_test.js
0 → 100644
| 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 | +}); |
public/static/jsondiff/jsl/jsl.format.js
0 → 100644
| 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 | +}); |
public/static/jsondiff/jsl/jsl.parser.js
0 → 100644
| 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}() |
public/static/jsondiff/one.json
0 → 100644
| 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"]} |
public/static/jsondiff/proxy.php
0 → 100644
| 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 | +?> |
public/static/jsondiff/qunit-2.1.1.css
0 → 100644
| 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 | +} |
public/static/jsondiff/qunit-2.1.1.js
0 → 100644
This diff could not be displayed because it is too large.
public/static/jsondiff/reset.css
0 → 100644
| 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 | +} |
public/static/jsondiff/throbber.css
0 → 100644
| 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 | +} |
public/static/jsondiff/two.json
0 → 100644
| 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"]} |
vendor/textdiff/TextDiff.php
0 → 100644
| 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']) ? ' ' : $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']) ? ' ' : $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 | +} |
vendor/textdiff/jdd-master.zip
0 → 100644
No preview for this file type
vendor/textdiff/jdd-master/.jshintrc
0 → 100644
| 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 | +} |
vendor/textdiff/jdd-master/LICENSE.txt
0 → 100644
| 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 | + |
vendor/textdiff/jdd-master/README.md
0 → 100644
| 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) |
vendor/textdiff/jdd-master/favicon.ico
0 → 100644
No preview for this file type
vendor/textdiff/jdd-master/index-test.html
0 → 100644
| 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> |
vendor/textdiff/jdd-master/index.html
0 → 100644
| 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> |
vendor/textdiff/jdd-master/jdd.css
0 → 100644
| 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 | +} |
-
Please register or login to post a comment