Commit ccbaa06e7f43a8822849876066a9ddc29445339d

Authored by pengye
2 parents a91fc7d2 a988d490

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

Showing 45 changed files with 7034 additions and 28 deletions
... ... @@ -57,6 +57,265 @@ class System extends Common
* 获取日志列表
*/
public function log(){
$identity = Db::query("SELECT identity FROM `xt-member-two-LINE`.xt_dc_dz_log_action GROUP BY identity");
if(Request::isAjax()){
$data = Request::except('file');
$key = input('post.key') ? input('post.key') : "";
$key = $data['key'] ? " a.`name` LIKE '%".$data['key']."%'" : '1=1';
$nickname = $data['nickname'] ? " AND a.nickname LIKE '%".$data['nickname']."%'" : '';
$data_time= $data['time'] ? explode(' - ', $data['time']) : array(date("Y-m-d 00:00:00"),date("Y-m-d H:i:s"));
$time = $data_time ? "AND a.cdate BETWEEN '".$data_time[0]."' AND '".$data_time[1]."'" : '';
$iden = ($data['iden'] && $data['iden']!=-1) ? " AND a.identity = ".$data['iden'] : "";
$channel = ($data['channel'] && $data['channel']!=-1) ? " AND rel.type_id = ".$data['channel'] : "";
$log_info = Db::query("SELECT
a.*,
h.field,
h.old,
h.new,
h.differ,
rel.type_id
FROM
`xt-member-two-LINE`.xt_dc_dz_log_action AS a
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
WHERE
$key
$nickname
$iden
$channel
$time
order by
a.cdate desc
LIMIT ".$data['page'].",".$data['limit']."");
$total = Db::query("SELECT
count(1) as total
FROM
`xt-member-two-LINE`.xt_dc_dz_log_action AS a
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
WHERE
$key
$nickname
$iden
$channel
$time");
foreach ($log_info as $key => $value) {
switch ($value['device']) {
case '1':
$log_info[$key]['device'] = '小程序(C端)';
break;
case '2':
$log_info[$key]['device'] = '小程序(B端)';
break;
case '3':
$log_info[$key]['device'] = '铱方';
break;
case '4':
$log_info[$key]['device'] = '极钛';
break;
case '5':
$log_info[$key]['device'] = '酷比';
break;
case '6':
$log_info[$key]['device'] = '华为';
break;
default:
# code...
break;
}
switch ($value['field']) {
case 'status':
$log_info[$key]['old'] = lang('log')[$value['old']];
$log_info[$key]['new'] = lang('log')[$value['new']];
break;
case 'ddate':
$log_info[$key]['old'] = date("Y-m-d H:i:s",$value['old']);
$log_info[$key]['new'] = date("Y-m-d H:i:s",$value['new']);
break;
case 'action':
$log_info[$key]['old'] = $value['old']==0 ? '未发短信' : ($value['old']==1 ? '已发短信' : '已回访');
$log_info[$key]['new'] = $value['new']==0 ? '未发短信' : ($value['new']==1 ? '已发短信' : '已回访');
break;
default:
# code...
break;
}
//跳转页面逻辑判断
//规则:
//1、如果field字段存在 则直接显示变更前,变更后
//2、如果预定ID为0
$haystack = array('Android.Reserve.SetReserveInfo','App.Customer.SetReserveInfo');
if(array_key_exists($value['field'],lang('log'))){
$log_info[$key]['field'] = lang('log')[$value['field']];
}elseif($value['rid']==0){
$log_info[$key]['field'] = lang('log')['seller'];
}elseif(in_array($value['action'], $haystack)){
if($value['comment']){
$log_info[$key]['field'] = lang('log')['add_rid'];
$log_info[$key]['old'] = '<a href=/admin/system/detail?id='.$value["id"].'&tag=1 target="_blank">查看详情</a>';
$log_info[$key]['new'] = '<a href=/admin/system/detail?id='.$value["id"].'&tag=1 target="_blank">查看详情</a>';
}else{
$log_info[$key]['field'] = lang('log')['edi_rid'];
$log_info[$key]['old'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
$log_info[$key]['new'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
}
}else{
$log_info[$key]['field'] = lang('log')['edi_rid'];
$log_info[$key]['old'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
$log_info[$key]['new'] = '<a href=/admin/system/differ?id='.$value["id"].' target="_blank">查看详情</a>';
}
}
return ['code'=>0,'msg'=>'获取成功!','data'=>$log_info,'count'=>$total[0]['total'],'rel'=>1];
}
$idn = array();
foreach (array_column($identity, 'identity') as $key => $value) {
switch ($value) {
case '0':
$idn[$value] = '顾客';
break;
case '1':
$idn[$value] = '运营';
break;
case '2':
$idn[$value] = '代理商';
break;
case '3':
$idn[$value] = '商家人员';
break;
default:
# code...
break;
}
}
$tmp = array();
while (list($key, $val) = each($idn)) {
$tmp[] = array('key'=>$key,'val'=>$val);
}
$this->assign('identity', $tmp);
return $this->fetch();
}
public function differ(){
$data = Request::except('file');
if($data['id']){
$log_info = Db::query("SELECT
a.*,
h.field,
h.old,
h.new,
h.differ,
rel.type_id
FROM
`xt-member-two-LINE`.xt_dc_dz_log_action AS a
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
WHERE
a.id = ".$data['id']);
$log_info = $log_info[0];
if($log_info['comment']){
$log_info['comment'] = json_decode($log_info['comment'],true);
if($log_info['comment']['room_demand']){
$log_info['comment']['room_demand'] = json_decode($log_info['comment']['room_demand'],true);
}
}
if($log_info['old']){
$log_info['old'] = json_decode($log_info['old'],true);
if($log_info['old']['tableinfo']){
$log_info['old']['tableinfo'] = json_decode($log_info['old']['tableinfo'],true);
}
}
if($log_info['new']){
$log_info['new'] = json_decode($log_info['new'],true);
if($log_info['new']['tableinfo']){
$log_info['new']['tableinfo'] = json_decode($log_info['new']['tableinfo'],true);
}
}
$old = json_encode($log_info['old'],JSON_UNESCAPED_UNICODE);
$new = json_encode($log_info['new'],JSON_UNESCAPED_UNICODE);
}
$this->assign('html', $log_info);
$this->assign('new', $new);
$this->assign('old', $old);
return $this->fetch();
}
public function detail(){
$data = Request::except('file');
if($data['id']){
$log_info = Db::query("SELECT
a.*,
h.field,
h.old,
h.new,
h.differ,
rel.type_id
FROM
`xt-member-two-LINE`.xt_dc_dz_log_action AS a
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_log_history AS h ON a.id = h.aid
LEFT JOIN `xt-member-two-LINE`.xt_dc_dz_seller_type_rel AS rel ON a.sid = rel.sid
WHERE
a.id = ".$data['id']);
$log_info = $log_info[0];
if($log_info['comment']){
$log_info['comment'] = json_decode($log_info['comment'],true);
if($log_info['comment']['row']['tableinfo']){
$log_info['comment']['row']['tableinfo'] = json_decode($log_info['comment']['row']['tableinfo'],true);
}
}
switch ($log_info['device']) {
case '1':
$log_info['device'] = '小程序(C端)';
break;
case '2':
$log_info['device'] = '小程序(B端)';
break;
case '3':
$log_info['device'] = '铱方';
break;
case '4':
$log_info['device'] = '极钛';
break;
case '5':
$log_info['device'] = '酷比';
break;
case '6':
$log_info['device'] = '华为';
break;
default:
# code...
break;
}
if(array_key_exists($log_info['action'],lang('log'))){
if($data['tag']==1){
$log_info['action'] = lang('log')[$log_info['action']][0];
}else{
$log_info['action'] = lang('log')[$log_info['action']][1];
}
}
$log_info['rname'] = $log_info['comment']['row']['rname'] ? $log_info['comment']['row']['rname'] : '';
$log_info['sex'] = $log_info['comment']['row']['rname']==1 ? '先生' : ($log_info['comment']['row']['rname']==2 ? '女士' : '公司');
$log_info['phone'] = $log_info['comment']['row']['phone'] ? $log_info['comment']['row']['phone'] : '';
$log_info['cusman'] = $log_info['comment']['row']['cusman'] ? $log_info['comment']['row']['cusman'] : '';
$log_info['ddate'] = $log_info['comment']['row']['ddate'] ? date("Y-m-d H:i:s",$log_info['comment']['row']['ddate']) : '';
$log_info['cdate'] = $log_info['comment']['row']['cdate'] ? date("Y-m-d H:i:s",$log_info['comment']['row']['cdate']) : '';
$log_info['status'] = lang('log')[$log_info['comment']['row']['status']];
$log_info['ddate_solt'] = $log_info['comment']['row']['ddate_solt'] ? $log_info['comment']['row']['ddate_solt'] : '';
$log_info['is_set_ddate'] = $log_info['comment']['row']['is_set_ddate'] ==1 ? '是' : '否';
}
$this->assign('html', $log_info);
return $this->fetch();
}
//阿里云日志log
public function aliyunlog(){
//基础参数
/* 使用autoloader类自动加载所有需要的PHP模块。注意使用合适的路径指向autoloader类所在文件*/
$DOCUMENT_ROOT = str_replace('public','',$_SERVER['DOCUMENT_ROOT']);
... ...
<?php
return [
/*********** 日志记录 **********/
'log'=>array(
'ddate' => '到店时间',
'status' => '预定状态',
'action' => '短信回访',
'add_rid' => '新增预定',
'edi_rid' => '修改预定',
'App.Customer.SetReserveInfo' => '录入了一笔订单',
'App.Customer.SetReserveInfoById' => '修改了一笔订单',
'App.Customer.SetDdate' => '设置了到店时间',
'App.Customer.SetReserveStatus' => '设置了预定状态',
'App.Reserve.NewExportReserve' => '导出了一份预定',
'App.Reserve.NewSetReserveInfo' => '录入了一笔订单',
'App.Reserve.NewSetReserveInfo' => '修改了一笔订单',
'App.Reserve.SetReserveCusman' => '设置了客户经理',
'App.Reserve.SetReserveStatus' => '设置了预定状态',
'App.Reserve.UpdateAction' => '回访了短信',
'App.Seller.AddSeller' => '添加了一个商家',
'App.Seller.setSellerById' => '修改了商家信息',
'App.Seller.InSterAuthUser' => '移交了店铺',
'Android.Customer.SetReserveInfo' => '修改了一笔订单',
'Android.Customer.SetReserveStatus' => '设置了预定状态',
'Android.Reserve.SetReserveInfo' => array('录入了一笔订单','修改了一笔订单'),
'Android.Reserve.SetReserveStatus' => '设置了预定状态',
'Android.Reserve.UpdateAction' => '回访了短信',
'0' => '待确认',
'1' => '已定座',
'2' => '确认到店',
'3' => '用餐完成',
'4' => '取消预定',
'5' => '分配包间',
'6' => '拒单',
'field' => '预定描述',
'sid' => '商家编号',
'pnum' => '用餐人数',
'rdate' => '预定开始时间',
'edate' => '预定结束时间',
'ddate' => '预定到店时间',
'channel' => '预定来源渠道',
'type' => '预定类型',
'sex' => '用户性别',
'phone' => '用户电话',
'code' => '编码CODE',
'cusdem' => '用餐描述',
'ddate_solt' => '用餐时段',
'room_demand'=> '包间ID',
'openid' => '用户openid',
),
'edited' => '编辑了',
'deled' => '删除了',
'added' => '添加了',
'App.Customer.DelCustomerInfo' => '删除了联系人',
/*********** 日志记录 **********/
'hello' => '欢迎使用',
'adminInfo' => '个人信息',
... ...
{include file="common/head"/}
<div class="admin-main layui-anim layui-anim-upbit">
<title>纳极科技 - 变更详情</title>
<div class="header">
<h1>订单详情</h1>
<h3>操作人:{$html.nickname} {$html.action} | 修改时间:{$html.cdate}</h3>
</div>
<div class="table-responsive">
<table class="layui-table" lay-even lay-skin="line">
<colgroup>
<col width="40%">
<col>
</colgroup>
<thead>
<tr>
<th class="text-center" colspan="2">{:lang('systemInfo')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>商家编号</td>
<td>{$html.sid}</td>
</tr>
<tr>
<td>预定编号</td>
<td>{$html.rid}</td>
</tr>
<tr>
<td>操作人昵称</td>
<td>{$html.nickname}</td>
</tr>
<tr>
<td>商家名称</td>
<td>{$html.name}</td>
</tr>
<tr>
<td>操作设备</td>
<td>{$html.device}</td>
</tr>
<tr>
<td>预定人</td>
<td>{$html.rname}{$html.sex}</td>
</tr>
<tr>
<td>电话</td>
<td>{$html.phone}</td>
</tr>
</tbody>
</table>
订单的一生:
xxxxx..
</div>
</div>
{include file="common/foot"/}
\ No newline at end of file
... ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>纳极科技 - 变更详情</title>
<link rel="stylesheet" href="/static/jsondiff/reset.css" type="text/css" media="screen">
<link rel="stylesheet" href="/static/jsondiff/throbber.css" type="text/css" media="screen">
<link rel="stylesheet" href="/static/jsondiff/jdd.css" type="text/css" media="screen">
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js" charset="utf-8"></script>
<script src="/static/jsondiff/jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
<script src="/static/jsondiff/jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
<script src="/static/jsondiff/jdd.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="main">
<div class="header">
<h1>变更详情</h1>
<h3>操作人:{$html.nickname} | 修改时间:{$html.cdate}</h3>
</div>
<div class="initContainer">
<div class="left">
<textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1">{$old}</textarea>
<pre id="errorLeft" class="error"></pre>
</div>
<div class="center">
<button id="compare" tabindex="3">Compare</button>
<div class="throbber-loader"></div>
<br/><br/>
or try some<br /><a href="#" id="sample">sample data</a>
</div>
<div class="right">
<textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2">{$new}</textarea>
<pre id="errorRight" class="error"></pre>
</div>
</div>
<div class="diffcontainer">
<div id="report">
</div>
<pre id="out" class="left" class="codeBlock"></pre>
<pre id="out2" class="right" class="codeBlock"></pre>
<ul id="toolbar" class="toolbar"></ul>
</div>
</div>
</body>
</html>
... ...
... ... @@ -4,10 +4,14 @@
<legend>{:$title}</legend>
</fieldset>
<div class="demoTable">
<!--商家:
商家:
<div class="layui-input-inline">
<input class="layui-input" name="key" id="key" placeholder="{:lang('pleaseEnter')}店铺名称" autocomplete="off">
</div>
昵称:
<div class="layui-input-inline">
<input class="layui-input" name="key" id="nickname" placeholder="{:lang('pleaseEnter')}用户昵称或者账号" autocomplete="off">
</div>
日期范围:
<div class="layui-input-inline" >
<input type="text" style = "width:300px"; class="layui-input" id="time" placeholder="默认为当天的统计情况">
... ... @@ -23,20 +27,18 @@
</select>
</div>
</div>
筛选条件:
身份:
<div class="layui-input-inline layui-form">
<div class="layui-input-8">
<select name="class" id="time_type" lay-filter="myselect">
<option value="1">录入时间</option>
<option value="2">到店时间</option>
<select name="class" id="iden" lay-filter="myselect">
<option value="-1">全部</option>
{volist name="identity" id="vo"}
<option value="{$vo.key}">{$vo.val}</option>
{/volist}
</select>
</div>
</div>-->
<!--地区:
<div class="layui-input-inline">
<input type="text" class="layui-input" id="area" placeholder="地区暂未开放">
</div>
<button class="layui-btn" id="search" data-type="reload">查询统计</button>-->
<button class="layui-btn" id="search" data-type="reload">查询统计</button>
</div>
<table class="layui-table" id="list" lay-filter="list"></table>
</div>
... ... @@ -47,7 +49,7 @@
layui.use(['table','form','laydate'], function() {
var table = layui.table,laydate = layui.laydate,$ = layui.jquery;
console.log(layui);
var tableIn = table.render({
var tableIn = table.render({
elem: '#list',
url: '{:url("log")}',
method: 'post',
... ... @@ -55,8 +57,15 @@
page:true,
toolbar: '#toolbarDemo',
cols: [[
{field: 'parm', align: 'center',title: '内容'},
{field: 'time',width:210, title: '时间'},
{field: 'rid', align: 'center',title: '预定ID'},
{field: 'sid', align: 'center',title: '商家ID'},
{field: 'name', align: 'center',title: '商家名字'},
{field: 'cdate',width:210, title: '变更时间'},
{field: 'nickname', align: 'center',title: '变更人'},
{field: 'device',width:210, title: '操作设备'},
{field: 'field',width:210, title: '变更字段'},
{field: 'old',width:210, title: '变更前'},
{field: 'new',width:210, title: '变更后'},
]],
done: function(res, curr, count){
//code
... ... @@ -66,7 +75,7 @@
,limit:15
});
/*//日期时间范围
//日期时间范围
laydate.render({
elem: '#time'
,type: 'datetime'
... ... @@ -77,23 +86,18 @@
$('#search').on('click', function() {
//关键字
var key = $('#key').val();
//选中的商家ID
var checkStatus = table.checkStatus('testReload'); //test即为参数id设定的值
var a = [];
$(checkStatus.data).each(function(i,o){
a.push(o.id);
});
console.log(a);
//昵称
var nickname = $('#nickname').val();
//日期范围
var time = $('#time').val();
console.log(time);
//商家来源
var channel = $('#channel').val();
console.log(channel);
//筛选条件
var time_type = $('#time_type').val();
console.log(time_type);
//身份
var iden = $('#iden').val();
console.log(iden);
//传参
tableIn.reload({ page: {page: 1},where: {key: key,sid: a,time: time,time_type: time_type,channel: channel}});
tableIn.reload({ page: {page: 1},where: {key: key,nickname: nickname,time: time,iden: iden,channel: channel}});
});
var $ = layui.$, active = {
... ... @@ -111,6 +115,6 @@
}
});
}
};*/
};
})
</script>
\ No newline at end of file
... ...
{
"curly": true,
"bitwise": false,
"eqeqeq": true,
"es3": true,
"forin": true,
"freeze": true,
"immed": true,
"indent": 4,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonbsp": true,
"nonew": true,
"quotmark": true,
"undef": true,
"strict": true,
"globalstrict": true,
"browser": true,
"jquery": true,
"devel": true,
"globals": {
"iac": false
},
"predef": [ "_","JSON", "jsl", "jdd", "QUnit" ],
"maxparams": 14,
"maxdepth": 7,
"maxcomplexity": 15
}
... ...
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2015-2017] [Zack Grossbart]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
... ...
JSON Diff
==================================================
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.
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.
JSON Diff sorts, formats, and compares two JSON documents to find the actual semantic differences instead of just the text ones.
Try it out: [http://www.jsondiff.com](http://www.jsondiff.com)
Run the built-in unit tests: [http://www.jsondiff.com/index-test.html](http://www.jsondiff.com/index-test.html)
... ...
No preview for this file type
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSON Diff - The semantic JSON compare tool</title>
<link rel="stylesheet" href="reset.css" type="text/css" media="screen">
<link rel="stylesheet" href="throbber.css" type="text/css" media="screen">
<link rel="stylesheet" href="qunit-2.1.1.css" type="text/css" media="screen">
<link rel="stylesheet" href="jdd.css" type="text/css" media="screen">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
<script src="qunit-2.1.1.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd_test.js" type="text/javascript" charset="utf-8"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-26336682-2', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div id="main">
<div class="header">
<h1>JSON Diff - Unit Tests</h1>
<h3>The semantic JSON compare tool</h3>
<div class="weak">
<p>
Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
</p>
<p>
Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
</p>
<p>
Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
</p>
</div>
</div>
<div class="initContainer">
<div class="left">
<textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
<pre id="errorLeft" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')"></span>
</div>
<div class="center">
<button id="compare">Compare</button>
<div class="throbber-loader"></div>
<br/><br/><br/><br/>
or try some <a href="#" id="sample">sample data</a>
</div>
<div class="right">
<textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
<pre id="errorRight" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')"></span>
</div>
</div>
<div class="diffcontainer">
<div id="report">
</div>
<pre id="out" class="left" class="codeBlock"></pre>
<pre id="out2" class="right" class="codeBlock"></pre>
<ul id="toolbar" class="toolbar"></ul>
</div>
<br />
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</div>
</body>
</html>
... ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSON Diff - The semantic JSON compare tool</title>
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/reset.css" type="text/css" media="screen">
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/throbber.css" type="text/css" media="screen">
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/jdd.css" type="text/css" media="screen">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd.js" type="text/javascript" charset="utf-8"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-26336682-2', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div id="main">
<div class="header">
<h1>变更详情</h1>
<h3>The semantic JSON compare tool</h3>
</div>
<div class="initContainer">
<div class="left">
<textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1"></textarea>
<pre id="errorLeft" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')" tabindex="4"></span>
</div>
<div class="center">
<button id="compare" tabindex="3">Compare</button>
<div class="throbber-loader"></div>
<br/><br/>
or try some<br /><a href="#" id="sample">sample data</a>
</div>
<div class="right">
<textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2"></textarea>
<pre id="errorRight" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')" tabindex="5"></span>
</div>
</div>
<div class="diffcontainer">
<div id="report">
</div>
<pre id="out" class="left" class="codeBlock"></pre>
<pre id="out2" class="right" class="codeBlock"></pre>
<ul id="toolbar" class="toolbar"></ul>
</div>
</div>
</body>
</html>
... ...
body {
padding: 0;
margin: 0;
font-family: Grotesk, "Helvetica Neue", Helvetica, sans-serif;
font-size: .9rem;
}
h1 {
font-size: 3em;
line-height: 1;
margin-bottom: .1em;
font-weight: 300;
}
h3 {
font-size: 1.5em;
line-height: 1;
margin-bottom: 1em;
margin-top: 1em;
}
a {
text-decoration: none;
color: #36c;
}
a:hover {
text-decoration: underline;
}
#main {
margin: 25px auto;
width: 95%
}
pre.codeBlock, div.codeBlock {
overflow:auto;
margin:0 0 1em;
padding:.5em 1em;
}
pre.codeBlock .codeLine,
pre.codeBlock .line-number,
div.codeBlock .codeLine,
div.codeBlock .line-number {
font:normal normal 13px/16px monospace;
font-family: Monaco, monospace;
color:black;
display:block;
}
pre.codeBlock.codeBlock span.code, div.codeBlock span.code {
min-width: 97%;
display: inline-block;
}
pre.codeBlock .line-number, div.codeBlock .line-number {
margin:0 1em 0 -1em;
border-right:1px solid #eee;
color: #bbb;
text-align:right;
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
-o-user-select: none;
user-select: none;
padding-left: 0.2em;
padding-right: 0.2em;
}
pre.codeBlock .line-number span, div.codeBlock .line-number span {
display:block;
padding:0 .5em 0 1em;
}
pre.codeBlock .cl {
display:block;
clear:both;
}
div.diffcontainer {
position: relative;
margin: 0 auto;
display: none;
}
div.left {
width: 40%;
display: inline-block;
}
div.right {
width: 40%;
display: inline-block;
vertical-align: top;
}
div.center {
width: 14%;
display: inline-block;
vertical-align: top;
text-align: left;
margin-top: 20%;
margin-left: 2%;
text-align: center;
}
pre.codeBlock.left {
width: 40%;
display: inline-block;
vertical-align: top;
}
pre.codeBlock.right {
width: 40%;
display: inline-block;
vertical-align: top;
margin-left: 1em;
}
ul.toolbar {
width: 11%;
display: inline-block;
right: 10px;
top: 5em;
position: absolute;
}
#report .reportTitle {
border: thin solid lightgray;
border-radius: 4px;
margin-bottom: 1em;
padding: 5px;
background-color: rgba(50, 57, 88, 0.1);
color: black;
width: 15em;
}
ul.toolbar li {
border: thin solid lightgray;
border-radius: 4px;
margin-bottom: 1em;
padding: 5px;
background-color: rgba(50, 57, 88, 0.1);
color: black;
cursor: pointer;
line-height: 18px;
}
ul.toolbar li code {
margin: 0 2px;
padding: 0px 5px;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
color: #333;
line-height: 20px;
font-family: Monaco, monospace;
font-size: 0.85em;
}
#buttons {
white-space: nowrap;
margin-bottom: 1em;
}
#buttons a {
color: #8a6d3b;
font-size: 18px;
}
#buttons a.disabled {
color: #999;
cursor: auto;
}
#buttons a.disabled:hover {
text-decoration: none;
cursor: default;
}
#prevNextLabel {
width: 80%;
text-align: center;
display: inline-block;
}
div.gutter {
float: left;
}
div.codeLine span.diff {
cursor: pointer;
color: #c00;
}
div.codeLine span.eq_off, div.codeLine span.type_off, div.codeLine span.missing_off {
cursor: auto;
}
div.codeLine span.eq {
background: rgba(50, 57, 88, 0.1);
color: #333;
}
div.codeLine span.type {
background: rgba(220, 100, 100, 0.1);
}
div.codeLine span.missing {
background: rgba(0, 160, 80, 0.1);
color: green;
}
div.codeLine span.selected {
background: #cef;
color: #47e;
}
canvas.center {
position: absolute;
left: 40%;
width: 20%;
height: 100%;
top: 0;
}
textarea {
width: 100%;
min-height: 500px;
padding: 5px;
background: #f3f3f3;
border-radius: 4px;
resize: vertical;
white-space: pre;
word-wrap: normal;
}
textarea.error {
border-color: #FBC2C4;
}
pre.error {
background: #FBE3E4;
color: #D12F19;
border: thin solid #FBC2C4;
border-radius: 4px;
padding: 1em;
display: none;
}
span.filterBlock {
margin-bottom: 1em;
}
span.filterBlock input {
margin-left: 2em;
}
#report {
margin-bottom: 1em;
position: relative;
height: 4em;
}
#report button {
display: block;
position: absolute;
top: 0;
left: 56.5%;
border-color: #36c;
color: #36c;
}
button {
font-size: 1.2em;
color: #333;
border: 1px solid #333;
border-radius: 4px;
background: white;
padding: .5rem .7rem;
cursor: pointer;
}
.weak {
color: #333;
max-width: 25rem;
font-size: .9em;
position: absolute;
top: 29px;
left: 56.5%;
right: 3%;
padding: 0 1em 0 0;
}
p {
margin-bottom: 1em;
}
.header {
height: 8em;
}
span.fileInput {
margin-left: 2em;
margin-top: 0.5em;
display: inline-block;
}
.throbber-loader:not(:required) {
display: none;
}
.progress .throbber-loader:not(:required) {
display: block;
margin: 5px auto;
}
... ...
/*******************************************************************************
*
* Copyright 2015-2019 Zack Grossbart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
'use strict';
// utilites
//
/**
* Fixing typeof
* takes value and returns type of value
* @param value
* return typeof value
*/
function getType(value) {
if ((function () { return value && (value !== this); }).call(value)) {
//fallback on 'typeof' for truthy primitive values
return typeof value;
}
return ({}).toString.call(value).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}
/**
* Iterate over array of objects and call given callback for each item in the array
* Optionally may take this as scope
*
* @param array
* @param callback
* @param optional scope
*/
function forEach(array, callback, scope) {
for (var idx = 0; idx < array.length; idx++) {
callback.call(scope, array[idx], idx, array);
}
}
/**
* The jdd object handles all of the functions for the main page. It finds the diffs and manages
* the interactions of displaying them.
*/
/*global jdd:true */
var jdd = {
LEFT: 'left',
RIGHT: 'right',
EQUALITY: 'eq',
TYPE: 'type',
MISSING: 'missing',
diffs: [],
requestCount: 0,
/**
* Find the differences between the two objects and recurse into their sub objects.
*/
findDiffs: function (/*Object*/ config1, /*Object*/ data1, /*Object*/ config2, /*Object*/ data2) {
config1.currentPath.push('/');
config2.currentPath.push('/');
var key;
// no un-used vars
// var val;
if (data1.length < data2.length) {
/*
* This means the second data has more properties than the first.
* We need to find the extra ones and create diffs for them.
*/
for (key in data2) {
if (data2.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
if (!data1.hasOwnProperty(key)) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, '/' + key),
'The right side of this object has more items than the left side', jdd.MISSING));
}
}
}
}
/*
* Now we're going to look for all the properties in object one and
* compare them to object two
*/
for (key in data1) {
if (data1.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
config1.currentPath.push(key);
if (!data2.hasOwnProperty(key)) {
/*
* This means that the first data has a property which
* isn't present in the second data
*/
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Missing property <code>' + key + '</code> from the object on the right side', jdd.MISSING));
} else {
config2.currentPath.push(key);
jdd.diffVal(data1[key], config1, data2[key], config2);
config2.currentPath.pop();
}
config1.currentPath.pop();
}
}
config1.currentPath.pop();
config2.currentPath.pop();
/*
* Now we want to look at all the properties in object two that
* weren't in object one and generate diffs for them.
*/
for (key in data2) {
if (data2.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
if (!data1.hasOwnProperty(key)) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, key),
'Missing property <code>' + key + '</code> from the object on the left side', jdd.MISSING));
}
}
}
},
/**
* Generate the differences between two values. This handles differences of object
* types and actual values.
*/
diffVal: function (val1, config1, val2, config2) {
if (getType(val1) === 'array') {
jdd.diffArray(val1, config1, val2, config2);
} else if (getType(val1) === 'object') {
if (['array', 'string', 'number', 'boolean', 'null'].indexOf(getType(val2)) > -1) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边对象不一致', jdd.TYPE));
} else {
jdd.findDiffs(config1, val1, config2, val2);
}
} else if (getType(val1) === 'string') {
if (getType(val2) !== 'string') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边字符串类型不一致', jdd.TYPE));
} else if (val1 !== val2) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边的字符串不相等', jdd.EQUALITY));
}
} else if (getType(val1) === 'number') {
if (getType(val2) !== 'number') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边的数字不相等', jdd.TYPE));
} else if (val1 !== val2) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边的数字不相等', jdd.EQUALITY));
}
} else if (getType(val1) === 'boolean') {
jdd.diffBool(val1, config1, val2, config2);
} else if (getType(val1) === 'null' && getType(val2) !== 'null') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'两边类型不对', jdd.TYPE));
}
},
/**
* Arrays are more complex because we need to recurse into them and handle different length
* issues so we handle them specially in this function.
*/
diffArray: function (val1, config1, val2, config2) {
if (getType(val2) !== 'array') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be arrays', jdd.TYPE));
return;
}
if (val1.length < val2.length) {
/*
* Then there were more elements on the right side and we need to
* generate those differences.
*/
for (var i = val1.length; i < val2.length; i++) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, '[' + i + ']'),
'Missing element <code>' + i + '</code> from the array on the left side', jdd.MISSING));
}
}
val1.forEach(function (arrayVal, index) {
if (val2.length <= index) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1, '[' + index + ']'),
config2, jdd.generatePath(config2),
'Missing element <code>' + index + '</code> from the array on the right side', jdd.MISSING));
} else {
config1.currentPath.push('/[' + index + ']');
config2.currentPath.push('/[' + index + ']');
if (getType(val2) === 'array') {
/*
* If both sides are arrays then we want to diff them.
*/
jdd.diffVal(val1[index], config1, val2[index], config2);
}
config1.currentPath.pop();
config2.currentPath.pop();
}
});
},
/**
* We handle boolean values specially because we can show a nicer message for them.
*/
diffBool: function (val1, config1, val2, config2) {
if (getType(val2) !== 'boolean') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be booleans', jdd.TYPE));
} else if (val1 !== val2) {
if (val1) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'The left side is <code>true</code> and the right side is <code>false</code>', jdd.EQUALITY));
} else {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'The left side is <code>false</code> and the right side is <code>true</code>', jdd.EQUALITY));
}
}
},
/**
* Format the object into the output stream and decorate the data tree with
* the data about this object.
*/
formatAndDecorate: function (/*Object*/ config, /*Object*/ data) {
if (getType(data) === 'array') {
jdd.formatAndDecorateArray(config, data);
return;
}
jdd.startObject(config);
config.currentPath.push('/');
var props = jdd.getSortedProperties(data);
/*
* If the first set has more than the second then we will catch it
* when we compare values. However, if the second has more then
* we need to catch that here.
*/
props.forEach(function (key) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '"' + jdd.unescapeString(key) + '": ';
config.currentPath.push(key);
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
jdd.formatVal(data[key], config);
config.currentPath.pop();
});
jdd.finishObject(config);
config.currentPath.pop();
},
/**
* Format the array into the output stream and decorate the data tree with
* the data about this object.
*/
formatAndDecorateArray: function (/*Object*/ config, /*Array*/ data) {
jdd.startArray(config);
/*
* If the first set has more than the second then we will catch it
* when we compare values. However, if the second has more then
* we need to catch that here.
*/
data.forEach(function (arrayVal, index) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
config.paths.push({
path: jdd.generatePath(config, '[' + index + ']'),
line: config.line
});
config.currentPath.push('/[' + index + ']');
jdd.formatVal(arrayVal, config);
config.currentPath.pop();
});
jdd.finishArray(config);
config.currentPath.pop();
},
/**
* Generate the start of the an array in the output stream and push in the new path
*/
startArray: function (config) {
config.indent++;
config.out += '[';
if (config.paths.length === 0) {
/*
* Then we are at the top of the array and we want to add
* a path for it.
*/
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
}
if (config.indent === 0) {
config.indent++;
}
},
/**
* Finish the array, outdent, and pop off all the path
*/
finishArray: function (config) {
if (config.indent === 0) {
config.indent--;
}
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']';
if (config.indent !== 0) {
config.out += ',';
} else {
config.out += jdd.newLine(config);
}
},
/**
* Generate the start of the an object in the output stream and push in the new path
*/
startObject: function (config) {
config.indent++;
config.out += '{';
if (config.paths.length === 0) {
/*
* Then we are at the top of the object and we want to add
* a path for it.
*/
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
}
if (config.indent === 0) {
config.indent++;
}
},
/**
* Finish the object, outdent, and pop off all the path
*/
finishObject: function (config) {
if (config.indent === 0) {
config.indent--;
}
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '}';
if (config.indent !== 0) {
config.out += ',';
} else {
config.out += jdd.newLine(config);
}
},
/**
* Format a specific value into the output stream.
*/
formatVal: function (val, config) {
if (getType(val) === 'array') {
config.out += '[';
config.indent++;
val.forEach(function (arrayVal, index) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
config.paths.push({
path: jdd.generatePath(config, '[' + index + ']'),
line: config.line
});
config.currentPath.push('/[' + index + ']');
jdd.formatVal(arrayVal, config);
config.currentPath.pop();
});
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']' + ',';
} else if (getType(val) === 'object') {
jdd.formatAndDecorate(config, val);
} else if (getType(val) === 'string') {
config.out += '"' + jdd.unescapeString(val) + '",';
} else if (getType(val) === 'number') {
config.out += val + ',';
} else if (getType(val) === 'boolean') {
config.out += val + ',';
} else if (getType(val) === 'null') {
config.out += 'null,';
}
},
/**
* When we parse the JSON string we end up removing the escape strings when we parse it
* into objects. This results in invalid JSON if we insert those strings back into the
* generated JSON. We also need to look out for characters that change the line count
* like new lines and carriage returns.
*
* This function puts those escaped values back when we generate the JSON output for the
* well known escape strings in JSON. It handles properties and values.
*
* This function does not handle unicode escapes. Unicode escapes are optional in JSON
* and the JSON output is still valid with a unicode character in it.
*/
unescapeString: function (val) {
if (val) {
return val.replace('\\', '\\\\') // Single slashes need to be replaced first
.replace(/\"/g, '\\"') // Then double quotes
.replace(/\n/g, '\\n') // New lines
.replace('\b', '\\b') // Backspace
.replace(/\f/g, '\\f') // Formfeed
.replace(/\r/g, '\\r') // Carriage return
.replace(/\t/g, '\\t'); // Horizontal tabs
} else {
return val;
}
},
/**
* Generate a JSON path based on the specific configuration and an optional property.
*/
generatePath: function (config, prop) {
var s = '';
config.currentPath.forEach(function (path) {
s += path;
});
if (prop) {
s += '/' + prop;
}
if (s.length === 0) {
return '/';
} else {
return s;
}
},
/**
* Add a new line to the output stream
*/
newLine: function (config) {
config.line++;
return '\n';
},
/**
* Sort all the relevant properties and return them in an alphabetical sort by property key
*/
getSortedProperties: function (/*Object*/ obj) {
var props = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
props.push(prop);
}
}
props = props.sort(function (a, b) {
return a.localeCompare(b);
});
return props;
},
/**
* Generate the diff and verify that it matches a JSON path
*/
generateDiff: function (config1, path1, config2, path2, /*String*/ msg, type) {
if (path1 !== '/' && path1.charAt(path1.length - 1) === '/') {
path1 = path1.substring(0, path1.length - 1);
}
if (path2 !== '/' && path2.charAt(path2.length - 1) === '/') {
path2 = path2.substring(0, path2.length - 1);
}
var pathObj1 = config1.paths.find(function (path) {
return path.path === path1;
});
var pathObj2 = config2.paths.find(function (path) {
return path.path === path2;
});
if (!pathObj1) {
throw 'Unable to find line number for (' + msg + '): ' + path1;
}
if (!pathObj2) {
throw 'Unable to find line number for (' + msg + '): ' + path2;
}
return {
path1: pathObj1,
path2: pathObj2,
type: type,
msg: msg
};
},
/**
* Get the current indent level
*/
getTabs: function (/*int*/ indent) {
var s = '';
for (var i = 0; i < indent; i++) {
s += ' ';
}
return s;
},
/**
* Remove the trailing comma from the output.
*/
removeTrailingComma: function (config) {
/*
* Remove the trailing comma
*/
if (config.out.charAt(config.out.length - 1) === ',') {
config.out = config.out.substring(0, config.out.length - 1);
}
},
/**
* Create a config object for holding differences
*/
createConfig: function () {
return {
out: '',
indent: -1,
currentPath: [],
paths: [],
line: 1
};
},
/**
* Format the output pre tags.
*/
formatPRETags: function () {
forEach($('pre'), function (pre) {
var codeBlock = $('<pre class="codeBlock"></pre>');
var lineNumbers = $('<div class="gutter"></div>');
codeBlock.append(lineNumbers);
var codeLines = $('<div></div>');
codeBlock.append(codeLines);
var addLine = function (line, index) {
var div = $('<div class="codeLine line' + (index + 1) + '"></div>');
lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
var span = $('<span class="code"></span');
span.text(line);
div.append(span);
codeLines.append(div);
};
var lines = $(pre).text().split('\n');
lines.forEach(addLine);
codeBlock.addClass($(pre).attr('class'));
codeBlock.attr('id', $(pre).attr('id'));
$(pre).replaceWith(codeBlock);
});
},
/**
* Format the text edits which handle the JSON input
*/
formatTextAreas: function () {
forEach($('textarea'), function (textarea) {
var codeBlock = $('<div class="codeBlock"></div>');
var lineNumbers = $('<div class="gutter"></div>');
codeBlock.append(lineNumbers);
var addLine = function (line, index) {
lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
};
var lines = $(textarea).val().split('\n');
lines.forEach(addLine);
$(textarea).replaceWith(codeBlock);
codeBlock.append(textarea);
});
},
handleDiffClick: function (line, side) {
var diffs = jdd.diffs.filter(function (diff) {
if (side === jdd.LEFT) {
return line === diff.path1.line;
} else if (side === jdd.RIGHT) {
return line === diff.path2.line;
} else {
return line === diff.path1.line || line === diff.path2.line;
}
});
$('pre.left span.code').removeClass('selected');
$('pre.right span.code').removeClass('selected');
$('ul.toolbar').text('');
diffs.forEach(function (diff) {
$('pre.left div.line' + diff.path1.line + ' span.code').addClass('selected');
$('pre.right div.line' + diff.path2.line + ' span.code').addClass('selected');
});
if (side === jdd.LEFT || side === jdd.RIGHT) {
jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
return diff.path1.line === line;
});
}
if (jdd.currentDiff === -1) {
jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
return diff.path2.line === line;
});
}
var buttons = $('<div id="buttons"><div>');
var prev = $('<a href="#" title="Previous difference" id="prevButton">&lt;</a>');
prev.addClass('disabled');
prev.click(function (e) {
e.preventDefault();
jdd.highlightPrevDiff();
});
buttons.append(prev);
buttons.append('<span id="prevNextLabel"></span>');
var next = $('<a href="#" title="Next difference" id="nextButton">&gt;</a>');
next.click(function (e) {
e.preventDefault();
jdd.highlightNextDiff();
});
buttons.append(next);
$('ul.toolbar').append(buttons);
jdd.updateButtonStyles();
jdd.showDiffDetails(diffs);
},
highlightPrevDiff: function () {
if (jdd.currentDiff > 0) {
jdd.currentDiff--;
jdd.highlightDiff(jdd.currentDiff);
jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
jdd.updateButtonStyles();
}
},
highlightNextDiff: function () {
if (jdd.currentDiff < jdd.diffs.length - 1) {
jdd.currentDiff++;
jdd.highlightDiff(jdd.currentDiff);
jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
jdd.updateButtonStyles();
}
},
updateButtonStyles: function () {
$('#prevButton').removeClass('disabled');
$('#nextButton').removeClass('disabled');
$('#prevNextLabel').text((jdd.currentDiff + 1) + ' of ' + (jdd.diffs.length));
if (jdd.currentDiff === 1) {
$('#prevButton').addClass('disabled');
} else if (jdd.currentDiff === jdd.diffs.length - 1) {
$('#nextButton').addClass('disabled');
}
},
/**
* Highlight the diff at the specified index
*/
highlightDiff: function (index) {
jdd.handleDiffClick(jdd.diffs[index].path1.line, jdd.BOTH);
},
/**
* Show the details of the specified diff
*/
showDiffDetails: function (diffs) {
diffs.forEach(function (diff) {
var li = $('<li></li>');
li.html(diff.msg);
$('ul.toolbar').append(li);
li.click(function () {
jdd.scrollToDiff(diff);
});
});
},
/**
* Scroll the specified diff to be visible
*/
scrollToDiff: function (diff) {
$('html, body').animate({
scrollTop: $('pre.left div.line' + diff.path1.line + ' span.code').offset().top
}, 0);
},
/**
* Process the specified diff
*/
processDiffs: function () {
var left = [];
var right = [];
jdd.diffs.forEach(function (diff) {
$('pre.left div.line' + diff.path1.line + ' span.code').addClass(diff.type).addClass('diff');
if (left.indexOf(diff.path1.line) === -1) {
$('pre.left div.line' + diff.path1.line + ' span.code').click(function () {
jdd.handleDiffClick(diff.path1.line, jdd.LEFT);
});
left.push(diff.path1.line);
}
$('pre.right div.line' + diff.path2.line + ' span.code').addClass(diff.type).addClass('diff');
if (right.indexOf(diff.path2.line) === -1) {
$('pre.right div.line' + diff.path2.line + ' span.code').click(function () {
jdd.handleDiffClick(diff.path2.line, jdd.RIGHT);
});
right.push(diff.path2.line);
}
});
jdd.diffs = jdd.diffs.sort(function (a, b) {
return a.path1.line - b.path1.line;
});
},
/**
* Validate the input against the JSON parser
*/
validateInput: function (json, side) {
try {
jsl.parser.parse(json);
if (side === jdd.LEFT) {
$('#errorLeft').text('').hide();
$('#textarealeft').removeClass('error');
} else {
$('#errorRight').text('').hide();
$('#textarearight').removeClass('error');
}
return true;
} catch (parseException) {
if (side === jdd.LEFT) {
$('#errorLeft').text(parseException.message).show();
$('#textarealeft').addClass('error');
} else {
$('#errorRight').text(parseException.message).show();
$('#textarearight').addClass('error');
}
return false;
}
},
/**
* Handle the file uploads
*/
handleFiles: function (files, side) {
var reader = new FileReader();
reader.onload = (function () {
return function (e) {
if (side === jdd.LEFT) {
$('#textarealeft').val(e.target.result);
} else {
$('#textarearight').val(e.target.result);
}
};
})(files[0]);
reader.readAsText(files[0]);
},
setupNewDiff: function () {
$('div.initContainer').show();
$('div.diffcontainer').hide();
$('div.diffcontainer pre').text('');
$('ul.toolbar').text('');
},
/**
* Generate the report section with the diff
*/
generateReport: function () {
var report = $('#report');
report.text('');
var newDiff = $('');
report.append(newDiff);
newDiff.click(function () {
jdd.setupNewDiff();
});
if (jdd.diffs.length === 0) {
report.append('<span>The two files were semantically identical.</span>');
return;
}
var typeCount = 0;
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
} else if (diff.type === jdd.TYPE) {
typeCount++;
}
});
var title = $('<div class="reportTitle"></div>');
if (jdd.diffs.length === 1) {
title.text('找到 ' + (jdd.diffs.length) + ' 处差别');
} else {
title.text('找到 ' + (jdd.diffs.length) + ' 处差别');
}
report.prepend(title);
var filterBlock = $('<span class="filterBlock">显示:</span>');
/*
* The missing checkbox
*/
if (missingCount > 0) {
var missing = $('<label><input id="showMissing" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (missingCount === 1) {
missing.append(missingCount + ' missing property');
} else {
missing.append(missingCount + ' missing properties');
}
missing.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.missing').addClass('missing_off').removeClass('missing');
} else {
$('span.code.diff.missing_off').addClass('missing').removeClass('missing_off');
}
});
filterBlock.append(missing);
}
/*
* The types checkbox
*/
if (typeCount > 0) {
var types = $('<label><input id="showTypes" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (typeCount === 1) {
types.append(typeCount + ' 处类型不在正确');
} else {
types.append(typeCount + ' 处类型不在正确');
}
types.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.type').addClass('type_off').removeClass('type');
} else {
$('span.code.diff.type_off').addClass('type').removeClass('type_off');
}
});
filterBlock.append(types);
}
/*
* The equals checkbox
*/
if (eqCount > 0) {
var eq = $('<label><input id="showEq" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (eqCount === 1) {
eq.append(eqCount + ' 处值不一样');
} else {
eq.append(eqCount + ' 处值不一样');
}
eq.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.eq').addClass('eq_off').removeClass('eq');
} else {
$('span.code.diff.eq_off').addClass('eq').removeClass('eq_off');
}
});
filterBlock.append(eq);
}
report.append(filterBlock);
},
/**
* Implement the compare button and complete the compare process
*/
compare: function () {
if (jdd.requestCount !== 0) {
/*
* This means we have a pending request and we just need to wait for that to finish.
*/
return;
}
$('body').addClass('progress');
$('#compare').prop('disabled', true);
var loadUrl = function (id, errId) {
if ($('#' + id).val().trim().substring(0, 4).toLowerCase() === 'http') {
jdd.requestCount++;
$.post('proxy.php',
{
'url': $('#' + id).val().trim()
}, function (responseObj) {
if (responseObj.error) {
$('#' + errId).text(responseObj.result).show();
$('#' + id).addClass('error');
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
} else {
$('#' + id).val(responseObj.content);
jdd.requestCount--;
jdd.compare();
}
}, 'json');
return true;
} else {
return false;
}
};
if (loadUrl('textarealeft', 'errorLeft')) {
return;
}
if (loadUrl('textarearight', 'errorRight')) {
return;
}
/*
* We'll start by running the text through JSONlint since it gives
* much better error messages.
*/
var leftValid = jdd.validateInput($('#textarealeft').val(), jdd.LEFT);
var rightValid = jdd.validateInput($('#textarearight').val(), jdd.RIGHT);
if (!leftValid || !rightValid) {
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
return;
}
$('div.initContainer').hide();
$('div.diffcontainer').show();
jdd.diffs = [];
var left = JSON.parse($('#textarealeft').val());
var right = JSON.parse($('#textarearight').val());
var config = jdd.createConfig();
jdd.formatAndDecorate(config, left);
$('#out').text(config.out);
var config2 = jdd.createConfig();
jdd.formatAndDecorate(config2, right);
$('#out2').text(config2.out);
jdd.formatPRETags();
config.currentPath = [];
config2.currentPath = [];
jdd.diffVal(left, config, right, config2);
jdd.processDiffs();
jdd.generateReport();
//console.log('diffs: ' + JSON.stringify(jdd.diffs));
if (jdd.diffs.length > 0) {
jdd.highlightDiff(0);
jdd.currentDiff = 0;
jdd.updateButtonStyles();
}
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
/*
* We want to switch the toolbar bar between fixed and absolute position when you
* scroll so you can get the maximum number of toolbar items.
*/
var toolbarTop = $('#toolbar').offset().top - 15;
$(window).scroll(function () {
if (toolbarTop < $(window).scrollTop()) {
$('#toolbar').css('position', 'fixed').css('top', '10px');
} else {
$('#toolbar').css('position', 'absolute').css('top', '');
}
});
},
/**
* Load in the sample data
*/
loadSampleData: function () {
$('#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}');
/*$('#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"}]');
$('#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"}]}');*/
$('#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"]}');
},
getParameterByName: function (name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
};
jQuery(document).ready(function () {
jdd.compare();
/*$('#compare').click(function () {
jdd.compare();
});*/
if (jdd.getParameterByName('left')) {
$('#textarealeft').val(jdd.getParameterByName('left'));
}
if (jdd.getParameterByName('right')) {
$('#textarearight').val(jdd.getParameterByName('right'));
}
if (jdd.getParameterByName('left') && jdd.getParameterByName('right')) {
jdd.compare();
}
$('#sample').click(function (e) {
e.preventDefault();
jdd.loadSampleData();
});
$(document).keydown(function (event) {
if (event.keyCode === 78 || event.keyCode === 39) {
/*
* The N key or right arrow key
*/
jdd.highlightNextDiff();
} else if (event.keyCode === 80 || event.keyCode === 37) {
/*
* The P key or left arrow key
*/
jdd.highlightPrevDiff();
}
});
});
// polyfills
// Array.prototype.find
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function (predicate) {
// 1. Let O be ? ToObject(this value).
if (this === null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
},
configurable: true,
writable: true
});
}
// Array.prototype.findIndex
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function (predicate) {
// 1. Let O be ? ToObject(this value).
if (this === null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return k.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return k;
}
// e. Increase k by 1.
k++;
}
// 7. Return -1.
return -1;
},
configurable: true,
writable: true
});
}
\ No newline at end of file
... ...
/*******************************************************************************
*
* Copyright 2015-2017 Zack Grossbart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
'use strict';
/**
* This is the main test harness for JSONDiff. It runs unit tests and integrations tests in the browser.
* To run these tests just access the index-test.html page. You can run the tests on the live site at
* http://www.jsondiff.com/index-test.html.
*
* Each test follows the same basic pattern: set the text of the left and right text areas then run the
* jdd.compare function. Once the compare completes we examine the JSON data in the jdd.diffs object to
* determine if the test completed successfully. As a last step we clear out the two text areas and call
* jdd.setupNewDiff to prepare for the next test.
*/
/**
* This is the basic compate test. It does a compare of our sample data and makes sure that we have the
* correct number of diffs of each type. It also checks a few random diffs to make sure they are correct.
*/
QUnit.test('Object compare tests', function (assert) {
$('#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}');
$('#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"]}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 20, 'Checking for the correct number of differences');
var typeCount = 0;
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
} else if (diff.type === jdd.TYPE) {
typeCount++;
}
});
assert.ok(eqCount === 4, 'Checking unequal values');
assert.ok(missingCount === 11, 'Checking missing values');
assert.ok(typeCount === 5, 'Checking incorrect types');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test focuses on finding differences between objects and arrays with similar data at the
* root of the JSON document.
*/
QUnit.test('Array to object compare tests', function (assert) {
$('#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"}]');
$('#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"}]}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test focuses on comparing arrays and makes sure that we can properly tell the difference
* between missing and unequal values.
*/
QUnit.test('Array compare tests', function (assert) {
$('#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"}]');
$('#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"}]');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 7, 'Checking for the correct number of differences');
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
}
});
assert.ok(eqCount === 6, 'Checking unequal values');
assert.ok(missingCount === 1, 'Checking missing values');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test works a lot like the array to object compare tests, but it switches the left and right
* values. We've had a few bugs that only show up when the difference is on a specific side.
*/
QUnit.test('Object to array compare tests', function (assert) {
$('#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"}]}');
$('#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"}]');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* These tests cover differences with whitespace in them. Specifically cases where there are new lines, backspaces,
* tabs, and other whitespace characters within the JSON keys and values. These values are escaped in the JSON source,
* but show up unescaped in the JSON object. We need to make sure we add them back and convert the line numbers properly.
*/
QUnit.test('Whitespace formatting tests', function (assert) {
$('#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"}');
$('#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"}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 19, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.MISSING, 'Checking missing property');
assert.ok(jdd.diffs[0].msg === 'Missing property <code>a\\bx</code> from the object on the left side', 'Checking property formatting');
assert.ok(jdd.diffs[9].type === jdd.MISSING, 'Checking missing property');
assert.ok(jdd.diffs[9].msg === 'Missing property <code>a\"b</code> from the object on the right side', 'Checking property formatting');
assert.ok(jdd.diffs[14].type === jdd.EQUALITY, 'Checking missing property');
assert.ok(jdd.diffs[18].type === jdd.EQUALITY, 'Checking missing property');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the right side of the compare.
*/
QUnit.test('Null array length tests - right side', function (assert) {
$('#textarealeft').val('{ "akey": [] }');
$('#textarearight').val('{ "akey": null }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the left side of the compare.
*/
QUnit.test('Null array length tests - left side', function (assert) {
$('#textarealeft').val('{ "akey": null }');
$('#textarearight').val('{ "akey": [] }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the right side of the compare.
*/
QUnit.test('Null object length tests - right side', function (assert) {
$('#textarealeft').val('{ "akey": {} }');
$('#textarearight').val('{ "akey": null }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the left side of the compare.
*/
QUnit.test('Null object length tests - left side', function (assert) {
$('#textarealeft').val('{ "akey": null }');
$('#textarearight').val('{ "akey": {} }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
QUnit.test('Escaped quotes test', function (assert) {
$('#textarealeft').val('{"link": "<a href=\\\"http://google.com/\\\">Google</a>"}');
$('#textarearight').val('{"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"}');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.EQUALITY, 'Checking correct type');
assert.ok($('pre.right div.line2 span').text().trim() === '"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"', 'Checking escaped quotes');
assert.ok($('pre.left div.line2 span').text().trim() === '"link": "<a href=\\\"http://google.com/\\\">Google</a>"', 'Checking escaped quotes');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
QUnit.done(function () {
$('div.initContainer').hide();
$('div.diffContainer').hide();
});
... ...
/*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 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;
/**
* 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).
*
**/
jsl.format = (function () {
function repeat(s, count) {
return new Array(count + 1).join(s);
}
function formatJson(json) {
var i = 0,
il = 0,
tab = " ",
newJson = "",
indentLevel = 0,
inString = false,
currentChar = null;
for (i = 0, il = json.length; i < il; i += 1) {
currentChar = json.charAt(i);
switch (currentChar) {
case '{':
case '[':
if (!inString) {
newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '}':
case ']':
if (!inString) {
indentLevel -= 1;
newJson += "\n" + repeat(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ',':
if (!inString) {
newJson += ",\n" + repeat(tab, indentLevel);
} else {
newJson += currentChar;
}
break;
case ':':
if (!inString) {
newJson += ": ";
} else {
newJson += currentChar;
}
break;
case ' ':
case "\n":
case "\t":
if (inString) {
newJson += currentChar;
}
break;
case '"':
if (i > 0 && json.charAt(i - 1) !== '\\') {
inString = !inString;
}
newJson += currentChar;
break;
default:
newJson += currentChar;
break;
}
}
return newJson;
}
return { "formatJson": formatJson };
}());
\ No newline at end of file
... ...
/*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 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;
/**
* Helper Function for Caret positioning
* Gratefully borrowed from the Masked Input Plugin by Josh Bush
* http://digitalbush.com/projects/masked-input-plugin
**/
$.fn.caret = function (begin, end) {
if (this.length === 0) {
return;
}
if (typeof begin === 'number') {
end = (typeof end === 'number') ? end : begin;
return this.each(function () {
if (this.setSelectionRange) {
this.focus();
this.setSelectionRange(begin, end);
} else if (this.createTextRange) {
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', begin);
range.select();
}
});
} else {
if (this[0].setSelectionRange) {
begin = this[0].selectionStart;
end = this[0].selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
begin = -range.duplicate().moveStart('character', -100000);
end = begin + range.text.length;
}
return {"begin": begin, "end": end};
}
};
/**
* jsl.interactions - provides support for interactions within JSON Lint.
*
**/
jsl.interactions = (function () {
var reformatParam,
reformat,
compress;
/******* UTILITY METHODS *******/
/**
* Get the Nth position of a character in a string
* @searchStr the string to search through
* @char the character to find
* @pos int the nth character to find, 1 based.
*
* @return int the position of the character found
**/
function getNthPos(searchStr, char, pos) {
var i,
charCount = 0,
strArr = searchStr.split(char);
if (pos === 0) {
return 0;
}
for (i = 0; i < pos; i++) {
if (i >= strArr.length) {
return -1;
}
// +1 because we split out some characters
charCount += strArr[i].length + char.length;
}
return charCount;
}
/**
* Get a URL parameter from the current windows URL.
* Courtesy Paul Oppenheim: http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
* @param name the parameter to retrieve
* @return string the url parameter's value, if any
**/
function getURLParameter(name) {
param = (new RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || ['', null])[1];
if (param) {
return decodeURIComponent(param);
} else {
return null;
}
}
/******* INTERACTION METHODS *******/
/**
* Validate the JSON we've been given, displaying an error or success message.
* @return void
**/
function validate() {
var lineNum,
lineMatches,
lineStart,
lineEnd,
jsonVal,
result;
jsonVal = $('#json_input').val();
try {
result = jsl.parser.parse(jsonVal);
if (result) {
$('#results').removeClass('error').addClass('success');
$('div.linedwrap').removeClass('redBorder').addClass('greenBorder');
$('#results').text('Valid JSON');
if (reformat) {
$('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, " "));
}
if (compress) {
$('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, ""));
}
} else {
alert("An unknown error occurred. Please contact Arc90.");
}
} catch (parseException) {
/**
* If we failed to validate, run our manual formatter and then re-validate so that we
* can get a better line number. On a successful validate, we don't want to run our
* manual formatter because the automatic one is faster and probably more reliable.
**/
try {
if (reformat) {
jsonVal = jsl.format.formatJson($('#json_input').val());
$('#json_input').val(jsonVal);
result = jsl.parser.parse($('#json_input').val());
}
} catch(e) {
parseException = e;
}
lineMatches = parseException.message.match(/line ([0-9]*)/);
if (lineMatches && typeof lineMatches === "object" && lineMatches.length > 1) {
lineNum = parseInt(lineMatches[1], 10);
if (lineNum === 1) {
lineStart = 0;
} else {
lineStart = getNthPos(jsonVal, "\n", lineNum - 1);
}
lineEnd = jsonVal.indexOf("\n", lineStart);
if (lineEnd < 0) {
lineEnd = jsonVal.length;
}
$('#json_input').focus().caret(lineStart, lineEnd);
}
$('#results').text(parseException.message);
$('#results').removeClass('success').addClass('error');
$('div.linedwrap').removeClass('greenBorder').addClass('redBorder');
}
$('#loadSpinner').hide();
}
/**
* Initialize variables, add event listeners, etc.
*
* @return void
**/
function init() {
reformatParam = getURLParameter('reformat');
reformat = reformatParam !== '0' && reformatParam !== 'no';
compress = reformatParam === 'compress',
jsonParam = getURLParameter('json');
if (compress) {
$('#headerText').html('JSONLint<span class="light">Compressor</span>');
}
if (!reformat) {
$('#headerText').html('JSONLint<span class="light">Lite</span>');
}
$('#validate').click(function () {
$('#results_header, #loadSpinner').show();
var jsonVal = $.trim($('#json_input').val());
if (jsonVal.substring(0, 4).toLowerCase() === "http") {
$.post("proxy.php", {"url": jsonVal}, function (responseObj) {
$('#json_input').val(responseObj.content);
validate();
}, 'json');
} else {
validate();
}
return false;
});
$('#json_input').keyup(function () {
$('div.linedwrap').removeClass('greenBorder').removeClass('redBorder');
}).linedtextarea({
selectedClass: 'lineselect'
}).focus();
$('#reset').click(function () {
$('#json_input').val('').focus();
});
$('#faqButton').click(function () {
$('#faq').slideToggle();
});
if (jsonParam) {
$('#json_input').val(jsonParam);
$('#validate').click();
}
}
return {
'init': init
};
}());
$(function () {
jsl.interactions.init();
});
... ...
var jsl = typeof(jsl) === 'undefined' ? {} : jsl;
/**
* JSON Lint Parser gratefully provided by Zach Carter
* https://github.com/zaach/jsonlint
**/
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}()
\ No newline at end of file
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
$url = filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
if (!$url || !preg_match("/^https?:/i", $url)) {
echo '{ "result": "Invalid URL. Please check your URL and try again.", "error": true }';
return;
}
$parsed_url = parse_url($url);
$scheme = strtolower($parsed_url['scheme']);
if (($scheme !== "http" && $scheme !== "https")) {
echo '{ "result": "Invalid URL - Please check your URL and try again.", "error": true }';
return;
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($data === false) {
echo '{ "result": "Unable to parse URL. Please check your URL and try again.", "error": true }';
return;
}
$contentLength = intval($info['download_content_length']);
$status = intval($info['http_code']);
if ($status >= 400) {
echo '{ "result": "URL returned bad status code ' . $status . '.", "error": true }';
return;
}
if ($contentLength >= 52428800) {
echo '{ "result": "URL content length greater than 10 megs (' . $contentLength . '). Validation not available for files this large.", "responseCode": "1" }';
return;
}
$response = new StdClass();
$response->status = $status;
$response->length = $contentLength;
$response->url = $info['url'];
$response->content = $data;
echo json_encode($response);
?>
... ...
/*!
* QUnit 2.1.1
* https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2017-01-06T01:52Z
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header (excluding toolbar) */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699A4;
background-color: #0D3349;
font-size: 1.5em;
line-height: 1em;
font-weight: 400;
border-radius: 5px 5px 0 0;
}
#qunit-header a {
text-decoration: none;
color: #C2CCD1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #FFF;
}
#qunit-banner {
height: 5px;
}
#qunit-filteredTest {
padding: 0.5em 1em 0.5em 1em;
color: #366097;
background-color: #F4FF77;
}
#qunit-userAgent {
padding: 0.5em 1em 0.5em 1em;
color: #FFF;
background-color: #2B81AF;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Toolbar */
#qunit-testrunner-toolbar {
padding: 0.5em 1em 0.5em 1em;
color: #5E740B;
background-color: #EEE;
}
#qunit-testrunner-toolbar .clearfix {
height: 0;
clear: both;
}
#qunit-testrunner-toolbar label {
display: inline-block;
}
#qunit-testrunner-toolbar input[type=checkbox],
#qunit-testrunner-toolbar input[type=radio] {
margin: 3px;
vertical-align: -2px;
}
#qunit-testrunner-toolbar input[type=text] {
box-sizing: border-box;
height: 1.6em;
}
.qunit-url-config,
.qunit-filter,
#qunit-modulefilter {
display: inline-block;
line-height: 2.1em;
}
.qunit-filter,
#qunit-modulefilter {
float: right;
position: relative;
margin-left: 1em;
}
.qunit-url-config label {
margin-right: 0.5em;
}
#qunit-modulefilter-search {
box-sizing: border-box;
width: 400px;
}
#qunit-modulefilter-search-container:after {
position: absolute;
right: 0.3em;
content: "\25bc";
color: black;
}
#qunit-modulefilter-dropdown {
/* align with #qunit-modulefilter-search */
box-sizing: border-box;
width: 400px;
position: absolute;
right: 0;
top: 50%;
margin-top: 0.8em;
border: 1px solid #D3D3D3;
border-top: none;
border-radius: 0 0 .25em .25em;
color: #000;
background-color: #F5F5F5;
z-index: 99;
}
#qunit-modulefilter-dropdown a {
color: inherit;
text-decoration: none;
}
#qunit-modulefilter-dropdown .clickable.checked {
font-weight: bold;
color: #000;
background-color: #D2E0E6;
}
#qunit-modulefilter-dropdown .clickable:hover {
color: #FFF;
background-color: #0D3349;
}
#qunit-modulefilter-actions {
display: block;
overflow: auto;
/* align with #qunit-modulefilter-dropdown-list */
font: smaller/1.5em sans-serif;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
box-sizing: border-box;
max-height: 2.8em;
display: block;
padding: 0.4em;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
float: right;
font: inherit;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
/* insert padding to align with checkbox margins */
padding-left: 3px;
}
#qunit-modulefilter-dropdown-list {
max-height: 200px;
overflow-y: auto;
margin: 0;
border-top: 2px groove threedhighlight;
padding: 0.4em 0 0;
font: smaller/1.5em sans-serif;
}
#qunit-modulefilter-dropdown-list li {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#qunit-modulefilter-dropdown-list .clickable {
display: block;
padding-left: 0.15em;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 1em 0.4em 1em;
border-bottom: 1px solid #FFF;
list-style-position: inside;
}
#qunit-tests > li {
display: none;
}
#qunit-tests li.running,
#qunit-tests li.pass,
#qunit-tests li.fail,
#qunit-tests li.skipped,
#qunit-tests li.aborted {
display: list-item;
}
#qunit-tests.hidepass {
position: relative;
}
#qunit-tests.hidepass li.running,
#qunit-tests.hidepass li.pass {
visibility: hidden;
position: absolute;
width: 0;
height: 0;
padding: 0;
border: 0;
margin: 0;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li.skipped strong {
cursor: default;
}
#qunit-tests li a {
padding: 0.5em;
color: #C2CCD1;
text-decoration: none;
}
#qunit-tests li p a {
padding: 0.25em;
color: #6B6464;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #FFF;
border-radius: 5px;
}
.qunit-source {
margin: 0.6em 0 0.3em;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: 0.2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 0.5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
color: #374E0C;
background-color: #E0F2BE;
text-decoration: none;
}
#qunit-tests ins {
color: #500;
background-color: #FFCACA;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: #000; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #FFF;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3C510C;
background-color: #FFF;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #FFF;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
}
#qunit-tests .fail { color: #000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: #008000; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/*** Aborted tests */
#qunit-tests .aborted { color: #000; background-color: orange; }
/*** Skipped tests */
#qunit-tests .skipped {
background-color: #EBECE9;
}
#qunit-tests .qunit-skipped-label {
background-color: #F4FF77;
display: inline-block;
font-style: normal;
color: #366097;
line-height: 1.8em;
padding: 0 0.5em;
margin: -0.4em 0.4em -0.4em 0;
}
/** Result */
#qunit-testresult {
color: #2B81AF;
background-color: #D2E0E6;
border-bottom: 1px solid #FFF;
}
#qunit-testresult .clearfix {
height: 0;
clear: both;
}
#qunit-testresult .module-name {
font-weight: 700;
}
#qunit-testresult-display {
padding: 0.5em 1em 0.5em 1em;
width: 85%;
float:left;
}
#qunit-testresult-controls {
padding: 0.5em 1em 0.5em 1em;
width: 10%;
float:left;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}
... ...
This diff could not be displayed because it is too large.
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
... ...
@-moz-keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
@-webkit-keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
@keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
/* :not(:required) hides these rules from IE9 and below */
.throbber-loader:not(:required) {
-moz-animation: throbber-loader 2000ms 300ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 300ms infinite ease-out;
animation: throbber-loader 2000ms 300ms infinite ease-out;
background: #dde2e7;
display: inline-block;
position: relative;
text-indent: -9999px;
width: 0.9em;
height: 1.5em;
margin: 0 1.6em;
}
.throbber-loader:not(:required):before, .throbber-loader:not(:required):after {
background: #dde2e7;
content: '\x200B';
display: inline-block;
width: 0.9em;
height: 1.5em;
position: absolute;
top: 0;
}
.throbber-loader:not(:required):before {
-moz-animation: throbber-loader 2000ms 150ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 150ms infinite ease-out;
animation: throbber-loader 2000ms 150ms infinite ease-out;
left: -1.6em;
}
.throbber-loader:not(:required):after {
-moz-animation: throbber-loader 2000ms 450ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 450ms infinite ease-out;
animation: throbber-loader 2000ms 450ms infinite ease-out;
right: -1.6em;
}
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
class TextDiff
{
private static $word_reg = null;
private $old = null;
private $new = null;
private $is_differ = false;
private $data = null;
private $table_html = null;
private $list_html = null;
function __construct($old, $new)
{
$this->setWordReg();
$this->old = $old;
$this->new = $new;
$this->data = $this->getDiffData();
}
public function getDiffArray($old, $new) {
$matrix = array();
$max = $old_max = $new_max = 0;
foreach($old as $old_key => $old_value) {
foreach(array_keys($new, $old_value) as $new_key) {
if (!empty($matrix[$old_key - 1][$new_key - 1])) {
$matrix[$old_key][$new_key] = $matrix[$old_key - 1][$new_key - 1] + 1;
} else {
$matrix[$old_key][$new_key] = 1;
}
if ($matrix[$old_key][$new_key] > $max) {
$max = $matrix[$old_key][$new_key];
$old_max = $old_key + 1 - $max;
$new_max = $new_key + 1 - $max;
}
}
}
if($max == 0) {
if ($old == $new) {
return $old;
} else {
return array(
array(
'source' => $old,
'change' => $new
)
);
}
} else {
$arr1 = $this->getDiffArray(array_slice($old, 0, $old_max), array_slice($new, 0, $new_max));
$arr2 = array_slice($new, $new_max, $max);
$arr3 = $this->getDiffArray(array_slice($old, $old_max + $max), array_slice($new, $new_max + $max));
return array_merge($arr1, $arr2, $arr3);
}
}
private function getDiffData()
{
if (empty($this->old) && empty($this->new)) {
return null;
}
$line_diff = $this->getLineDiff($this->old, $this->new);
// ダミー行を除去
$line_diff = array_filter($line_diff, function($val){return (false !== $val);});
$line_diff = array_values($line_diff);
$lines = array();
$line_count = 1;
foreach($line_diff as $line) {
if (is_array($line) && array_key_exists('change', $line)) {
$this->is_differ = true;
$words = $this->getWordDiff($line['source'], $line['change']);
$lines[$line_count] = array(
'line' => $line_count,
'differ' => true,
'source' => implode('', $line['source']),
'change' => implode('', $line['change']),
'words' => $words,
);
++$line_count;
} else {
$lines[$line_count] = array(
'line' => $line_count,
'differ' => false,
'source' => $line,
'change' => $line,
'words' => null
);
++$line_count;
}
}
return $lines;
}
private function getLineDiff($old, $new)
{
$old = str_replace("\r\n", "\n", $old);
$new = str_replace("\r\n", "\n", $new);
// 差異の行が続くと別の行にならない場合があるため、
// 各行の後にダミー行(false)を入れる
$old_lines = array();
foreach(explode("\n", $old) as $val) {
$old_lines[] = $val;
$old_lines[] = false;
}
$new_lines = array();
foreach(explode("\n", $new) as $val) {
$new_lines[] = $val;
$new_lines[] = false;
}
return $this->getDiffArray($old_lines, $new_lines);
}
private function getWordDiff($old_array, $new_array)
{
$words = array();
$count = 0;
foreach($old_array as $key => $source) {
$change = (!empty($new_array[$key]) ? $new_array[$key] : '');
$s_source = $this->getSeparatedWord($source);
$s_change = $this->getSeparatedWord($change);
$diff = $this->getDiffArray($s_source, $s_change);
foreach($diff as $k => $val) {
if (is_array($val) && array_key_exists('change', $val)) {
$words[] = array(
'source' => implode('', $val['source']),
'change' => implode('', $val['change']),
);
++$count;
} else {
$before = $count -1;
if(@array_key_exists($before, $words) && !@array_key_exists('source', $words[$before])) {
$words[$before] .= $val;
} else {
$words[] = $val;
++$count;
}
}
}
}
return $words;
}
private function setWordReg()
{
if (null === self::$word_reg) {
$regs = array(
'々〇〻\x{3220}-\x{3244}\x{3280}-\x{32B0}\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}', // 漢字
'ぁ-ん~ー', // ひらがな
'ァ-ヴ~ー', // カタカナ
'ヲ-゚', // 半角カナ
'\!-\~', // 英数記号
);
$reg = '['.implode(']+|[', $regs).']+';
$reg .= '|[^'.implode('', $regs).']+';
self::$word_reg = $reg;
}
}
private function getSeparatedWord($str){
preg_match_all('/'.self::$word_reg.'/iu', $str, $matches);
return $matches[0];
}
public function isDiffer()
{
return $this->is_differ;
}
public function getData()
{
return $this->data;
}
public function getHtml()
{
return $this->getTableHtml();
}
public function getTableHtml()
{
if (empty($this->data)) {
return null;
} elseif(!empty($this->table_html)) {
return $this->table_html;
}
$source = '<table class="differ -differ-table -differ-source">';
$change = '<table class="differ -differ-table -differ-change">';
foreach($this->data as $line) {
if (empty($line['line'])) {
continue;
}
if (empty($line['differ'])) {
$html = '<tr class="-line -no-differ">';
$html .= '<td class="-number">'.$line['line'].'</td>';
$html .= '<td class="-text">'.(empty($line['source']) ? '&nbsp;' : $line['source']).'</td>';
$source .= $html;
$change .= $html;
} else {
$html = '<tr class="-line -is-differ">';
$html .= '<td class="-number">'.$line['line'].'</td>';
$html .= '<td class="-text">';
$source .= $html;
$change .= $html;
foreach($line['words'] as $val) {
if (is_array($val) && array_key_exists('source', $val)) {
$source .= '<span class="-word -source">'.$val['source'].'</span>';
} else {
$source .= $val;
}
}
foreach($line['words'] as $val) {
if (is_array($val) && array_key_exists('change', $val)) {
$change .= '<span class="-word -change">'.$val['change'].'</span>';
} else {
$change .= $val;
}
}
$html = '</td>';
$source .= $html;
$change .= $html;
}
$html = '</tr>';
$source .= $html;
$change .= $html;
}
$html = '</table>';
$source .= $html;
$change .= $html;
return $this->table_html = array(
'source' => $source,
'change' => $change,
);
}
public function getListHtml()
{
if (empty($this->data)) {
return null;
} elseif(!empty($this->list_html)) {
return $this->list_html;
}
$source = '<ol class="differ -differ-list -differ-source">';
$change = '<ol class="differ -differ-list -differ-change">';
foreach($this->data as $line) {
if (!$line['differ']) {
$html = '<li class="-line -no-differ">';
$html .= '<div class="-text">'.(empty($line['source']) ? '&nbsp;' : $line['source']).'</div>';
$source .= $html;
$change .= $html;
} else {
$html = '<li class="-line -is-differ">';
$html .= '<div class="-text">';
$source .= $html;
$change .= $html;
foreach($line['words'] as $val) {
if (empty($val['change'])) {
$source .= $val;
} else {
$source .= '<span class="-word -source">'.$val['source'].'</span>';
}
}
foreach($line['words'] as $val) {
if (empty($val['change'])) {
$change .= $val;
} else {
$change .= '<span class="-word -change">'.$val['change'].'</span>';
}
}
$html = '</div>';
$html .= '</div>';
$source .= $html;
$change .= $html;
}
$html = '</li>';
$source .= $html;
$change .= $html;
}
$html = '</ol>';
$source .= $html;
$change .= $html;
return $this->list_html = array(
'source' => $source,
'change' => $change,
);
}
}
... ...
No preview for this file type
{
"curly": true,
"bitwise": false,
"eqeqeq": true,
"es3": true,
"forin": true,
"freeze": true,
"immed": true,
"indent": 4,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonbsp": true,
"nonew": true,
"quotmark": true,
"undef": true,
"strict": true,
"globalstrict": true,
"browser": true,
"jquery": true,
"devel": true,
"globals": {
"iac": false
},
"predef": [ "_","JSON", "jsl", "jdd", "QUnit" ],
"maxparams": 14,
"maxdepth": 7,
"maxcomplexity": 15
}
... ...
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2015-2017] [Zack Grossbart]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
... ...
JSON Diff
==================================================
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.
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.
JSON Diff sorts, formats, and compares two JSON documents to find the actual semantic differences instead of just the text ones.
Try it out: [http://www.jsondiff.com](http://www.jsondiff.com)
Run the built-in unit tests: [http://www.jsondiff.com/index-test.html](http://www.jsondiff.com/index-test.html)
... ...
No preview for this file type
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSON Diff - The semantic JSON compare tool</title>
<link rel="stylesheet" href="reset.css" type="text/css" media="screen">
<link rel="stylesheet" href="throbber.css" type="text/css" media="screen">
<link rel="stylesheet" href="qunit-2.1.1.css" type="text/css" media="screen">
<link rel="stylesheet" href="jdd.css" type="text/css" media="screen">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
<script src="qunit-2.1.1.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd_test.js" type="text/javascript" charset="utf-8"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-26336682-2', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div id="main">
<div class="header">
<h1>JSON Diff - Unit Tests</h1>
<h3>The semantic JSON compare tool</h3>
<div class="weak">
<p>
Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
</p>
<p>
Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
</p>
<p>
Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
</p>
</div>
</div>
<div class="initContainer">
<div class="left">
<textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
<pre id="errorLeft" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')"></span>
</div>
<div class="center">
<button id="compare">Compare</button>
<div class="throbber-loader"></div>
<br/><br/><br/><br/>
or try some <a href="#" id="sample">sample data</a>
</div>
<div class="right">
<textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON"></textarea>
<pre id="errorRight" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')"></span>
</div>
</div>
<div class="diffcontainer">
<div id="report">
</div>
<pre id="out" class="left" class="codeBlock"></pre>
<pre id="out2" class="right" class="codeBlock"></pre>
<ul id="toolbar" class="toolbar"></ul>
</div>
<br />
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</div>
</body>
</html>
... ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSON Diff - The semantic JSON compare tool</title>
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/reset.css" type="text/css" media="screen">
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/throbber.css" type="text/css" media="screen">
<link rel="stylesheet" href="http://www.dcadmin.com/static/jsondiff/jdd.css" type="text/css" media="screen">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.format.js" type="text/javascript" charset="utf-8"></script>
<script src="jsl/jsl.parser.js" type="text/javascript" charset="utf-8"></script>
<script src="jdd.js" type="text/javascript" charset="utf-8"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-26336682-2', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div id="main">
<div class="header">
<h1>JSON Diff</h1>
<h3>The semantic JSON compare tool</h3>
<div class="weak">
<p>
Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
</p>
<p>
Created by <a href="http://www.zackgrossbart.com">Zack Grossbart</a>. Get the <a href="https://github.com/zgrossbart/jdd">source code</a>.
</p>
<p>
Big thanks owed to the team behind <a href="http://www.jsonlint.com">JSONLint</a>.
</p>
</div>
</div>
<div class="initContainer">
<div class="left">
<textarea spellcheck="false" id="textarealeft" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="1"></textarea>
<pre id="errorLeft" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileLeft" onchange="jdd.handleFiles(this.files, 'left')" tabindex="4"></span>
</div>
<div class="center">
<button id="compare" tabindex="3">Compare</button>
<div class="throbber-loader"></div>
<br/><br/>
or try some<br /><a href="#" id="sample">sample data</a>
</div>
<div class="right">
<textarea spellcheck="false" class="right" id="textarearight" placeholder="Enter JSON to compare, enter an URL to JSON" tabindex="2"></textarea>
<pre id="errorRight" class="error"></pre>
<span class="fileInput">or <input type="file" id="fileRight" onchange="jdd.handleFiles(this.files, 'right')" tabindex="5"></span>
</div>
</div>
<div class="diffcontainer">
<div id="report">
</div>
<pre id="out" class="left" class="codeBlock"></pre>
<pre id="out2" class="right" class="codeBlock"></pre>
<ul id="toolbar" class="toolbar"></ul>
</div>
</div>
</body>
</html>
... ...
body {
padding: 0;
margin: 0;
font-family: Grotesk, "Helvetica Neue", Helvetica, sans-serif;
font-size: .9rem;
}
h1 {
font-size: 3em;
line-height: 1;
margin-bottom: .1em;
font-weight: 300;
}
h3 {
font-size: 1.5em;
line-height: 1;
margin-bottom: 1em;
margin-top: 1em;
}
a {
text-decoration: none;
color: #36c;
}
a:hover {
text-decoration: underline;
}
#main {
margin: 25px auto;
width: 95%
}
pre.codeBlock, div.codeBlock {
overflow:auto;
margin:0 0 1em;
padding:.5em 1em;
}
pre.codeBlock .codeLine,
pre.codeBlock .line-number,
div.codeBlock .codeLine,
div.codeBlock .line-number {
font:normal normal 13px/16px monospace;
font-family: Monaco, monospace;
color:black;
display:block;
}
pre.codeBlock.codeBlock span.code, div.codeBlock span.code {
min-width: 97%;
display: inline-block;
}
pre.codeBlock .line-number, div.codeBlock .line-number {
margin:0 1em 0 -1em;
border-right:1px solid #eee;
color: #bbb;
text-align:right;
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
-o-user-select: none;
user-select: none;
padding-left: 0.2em;
padding-right: 0.2em;
}
pre.codeBlock .line-number span, div.codeBlock .line-number span {
display:block;
padding:0 .5em 0 1em;
}
pre.codeBlock .cl {
display:block;
clear:both;
}
div.diffcontainer {
position: relative;
margin: 0 auto;
display: none;
}
div.left {
width: 40%;
display: inline-block;
}
div.right {
width: 40%;
display: inline-block;
vertical-align: top;
}
div.center {
width: 14%;
display: inline-block;
vertical-align: top;
text-align: left;
margin-top: 20%;
margin-left: 2%;
text-align: center;
}
pre.codeBlock.left {
width: 40%;
display: inline-block;
vertical-align: top;
}
pre.codeBlock.right {
width: 40%;
display: inline-block;
vertical-align: top;
margin-left: 1em;
}
ul.toolbar {
width: 11%;
display: inline-block;
right: 10px;
top: 5em;
position: absolute;
}
#report .reportTitle {
border: thin solid lightgray;
border-radius: 4px;
margin-bottom: 1em;
padding: 5px;
background-color: rgba(50, 57, 88, 0.1);
color: black;
width: 15em;
}
ul.toolbar li {
border: thin solid lightgray;
border-radius: 4px;
margin-bottom: 1em;
padding: 5px;
background-color: rgba(50, 57, 88, 0.1);
color: black;
cursor: pointer;
line-height: 18px;
}
ul.toolbar li code {
margin: 0 2px;
padding: 0px 5px;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
color: #333;
line-height: 20px;
font-family: Monaco, monospace;
font-size: 0.85em;
}
#buttons {
white-space: nowrap;
margin-bottom: 1em;
}
#buttons a {
color: #8a6d3b;
font-size: 18px;
}
#buttons a.disabled {
color: #999;
cursor: auto;
}
#buttons a.disabled:hover {
text-decoration: none;
cursor: default;
}
#prevNextLabel {
width: 80%;
text-align: center;
display: inline-block;
}
div.gutter {
float: left;
}
div.codeLine span.diff {
cursor: pointer;
color: #c00;
}
div.codeLine span.eq_off, div.codeLine span.type_off, div.codeLine span.missing_off {
cursor: auto;
}
div.codeLine span.eq {
background: rgba(50, 57, 88, 0.1);
color: #333;
}
div.codeLine span.type {
background: rgba(220, 100, 100, 0.1);
}
div.codeLine span.missing {
background: rgba(0, 160, 80, 0.1);
color: green;
}
div.codeLine span.selected {
background: #cef;
color: #47e;
}
canvas.center {
position: absolute;
left: 40%;
width: 20%;
height: 100%;
top: 0;
}
textarea {
width: 100%;
min-height: 500px;
padding: 5px;
background: #f3f3f3;
border-radius: 4px;
resize: vertical;
white-space: pre;
word-wrap: normal;
}
textarea.error {
border-color: #FBC2C4;
}
pre.error {
background: #FBE3E4;
color: #D12F19;
border: thin solid #FBC2C4;
border-radius: 4px;
padding: 1em;
display: none;
}
span.filterBlock {
margin-bottom: 1em;
}
span.filterBlock input {
margin-left: 2em;
}
#report {
margin-bottom: 1em;
position: relative;
height: 4em;
}
#report button {
display: block;
position: absolute;
top: 0;
left: 56.5%;
border-color: #36c;
color: #36c;
}
button {
font-size: 1.2em;
color: #333;
border: 1px solid #333;
border-radius: 4px;
background: white;
padding: .5rem .7rem;
cursor: pointer;
}
.weak {
color: #333;
max-width: 25rem;
font-size: .9em;
position: absolute;
top: 29px;
left: 56.5%;
right: 3%;
padding: 0 1em 0 0;
}
p {
margin-bottom: 1em;
}
.header {
height: 8em;
}
span.fileInput {
margin-left: 2em;
margin-top: 0.5em;
display: inline-block;
}
.throbber-loader:not(:required) {
display: none;
}
.progress .throbber-loader:not(:required) {
display: block;
margin: 5px auto;
}
... ...
/*******************************************************************************
*
* Copyright 2015-2019 Zack Grossbart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
'use strict';
// utilites
//
/**
* Fixing typeof
* takes value and returns type of value
* @param value
* return typeof value
*/
function getType(value) {
if ((function () { return value && (value !== this); }).call(value)) {
//fallback on 'typeof' for truthy primitive values
return typeof value;
}
return ({}).toString.call(value).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}
/**
* Iterate over array of objects and call given callback for each item in the array
* Optionally may take this as scope
*
* @param array
* @param callback
* @param optional scope
*/
function forEach(array, callback, scope) {
for (var idx = 0; idx < array.length; idx++) {
callback.call(scope, array[idx], idx, array);
}
}
/**
* The jdd object handles all of the functions for the main page. It finds the diffs and manages
* the interactions of displaying them.
*/
/*global jdd:true */
var jdd = {
LEFT: 'left',
RIGHT: 'right',
EQUALITY: 'eq',
TYPE: 'type',
MISSING: 'missing',
diffs: [],
requestCount: 0,
/**
* Find the differences between the two objects and recurse into their sub objects.
*/
findDiffs: function (/*Object*/ config1, /*Object*/ data1, /*Object*/ config2, /*Object*/ data2) {
config1.currentPath.push('/');
config2.currentPath.push('/');
var key;
// no un-used vars
// var val;
if (data1.length < data2.length) {
/*
* This means the second data has more properties than the first.
* We need to find the extra ones and create diffs for them.
*/
for (key in data2) {
if (data2.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
if (!data1.hasOwnProperty(key)) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, '/' + key),
'The right side of this object has more items than the left side', jdd.MISSING));
}
}
}
}
/*
* Now we're going to look for all the properties in object one and
* compare them to object two
*/
for (key in data1) {
if (data1.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
config1.currentPath.push(key);
if (!data2.hasOwnProperty(key)) {
/*
* This means that the first data has a property which
* isn't present in the second data
*/
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Missing property <code>' + key + '</code> from the object on the right side', jdd.MISSING));
} else {
config2.currentPath.push(key);
jdd.diffVal(data1[key], config1, data2[key], config2);
config2.currentPath.pop();
}
config1.currentPath.pop();
}
}
config1.currentPath.pop();
config2.currentPath.pop();
/*
* Now we want to look at all the properties in object two that
* weren't in object one and generate diffs for them.
*/
for (key in data2) {
if (data2.hasOwnProperty(key)) {
// no un-used vars
// val = data1[key];
if (!data1.hasOwnProperty(key)) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, key),
'Missing property <code>' + key + '</code> from the object on the left side', jdd.MISSING));
}
}
}
},
/**
* Generate the differences between two values. This handles differences of object
* types and actual values.
*/
diffVal: function (val1, config1, val2, config2) {
if (getType(val1) === 'array') {
jdd.diffArray(val1, config1, val2, config2);
} else if (getType(val1) === 'object') {
if (['array', 'string', 'number', 'boolean', 'null'].indexOf(getType(val2)) > -1) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be objects', jdd.TYPE));
} else {
jdd.findDiffs(config1, val1, config2, val2);
}
} else if (getType(val1) === 'string') {
if (getType(val2) !== 'string') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be strings', jdd.TYPE));
} else if (val1 !== val2) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both sides should be equal strings', jdd.EQUALITY));
}
} else if (getType(val1) === 'number') {
if (getType(val2) !== 'number') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be numbers', jdd.TYPE));
} else if (val1 !== val2) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both sides should be equal numbers', jdd.EQUALITY));
}
} else if (getType(val1) === 'boolean') {
jdd.diffBool(val1, config1, val2, config2);
} else if (getType(val1) === 'null' && getType(val2) !== 'null') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be nulls', jdd.TYPE));
}
},
/**
* Arrays are more complex because we need to recurse into them and handle different length
* issues so we handle them specially in this function.
*/
diffArray: function (val1, config1, val2, config2) {
if (getType(val2) !== 'array') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be arrays', jdd.TYPE));
return;
}
if (val1.length < val2.length) {
/*
* Then there were more elements on the right side and we need to
* generate those differences.
*/
for (var i = val1.length; i < val2.length; i++) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2, '[' + i + ']'),
'Missing element <code>' + i + '</code> from the array on the left side', jdd.MISSING));
}
}
val1.forEach(function (arrayVal, index) {
if (val2.length <= index) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1, '[' + index + ']'),
config2, jdd.generatePath(config2),
'Missing element <code>' + index + '</code> from the array on the right side', jdd.MISSING));
} else {
config1.currentPath.push('/[' + index + ']');
config2.currentPath.push('/[' + index + ']');
if (getType(val2) === 'array') {
/*
* If both sides are arrays then we want to diff them.
*/
jdd.diffVal(val1[index], config1, val2[index], config2);
}
config1.currentPath.pop();
config2.currentPath.pop();
}
});
},
/**
* We handle boolean values specially because we can show a nicer message for them.
*/
diffBool: function (val1, config1, val2, config2) {
if (getType(val2) !== 'boolean') {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'Both types should be booleans', jdd.TYPE));
} else if (val1 !== val2) {
if (val1) {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'The left side is <code>true</code> and the right side is <code>false</code>', jdd.EQUALITY));
} else {
jdd.diffs.push(jdd.generateDiff(config1, jdd.generatePath(config1),
config2, jdd.generatePath(config2),
'The left side is <code>false</code> and the right side is <code>true</code>', jdd.EQUALITY));
}
}
},
/**
* Format the object into the output stream and decorate the data tree with
* the data about this object.
*/
formatAndDecorate: function (/*Object*/ config, /*Object*/ data) {
if (getType(data) === 'array') {
jdd.formatAndDecorateArray(config, data);
return;
}
jdd.startObject(config);
config.currentPath.push('/');
var props = jdd.getSortedProperties(data);
/*
* If the first set has more than the second then we will catch it
* when we compare values. However, if the second has more then
* we need to catch that here.
*/
props.forEach(function (key) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '"' + jdd.unescapeString(key) + '": ';
config.currentPath.push(key);
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
jdd.formatVal(data[key], config);
config.currentPath.pop();
});
jdd.finishObject(config);
config.currentPath.pop();
},
/**
* Format the array into the output stream and decorate the data tree with
* the data about this object.
*/
formatAndDecorateArray: function (/*Object*/ config, /*Array*/ data) {
jdd.startArray(config);
/*
* If the first set has more than the second then we will catch it
* when we compare values. However, if the second has more then
* we need to catch that here.
*/
data.forEach(function (arrayVal, index) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
config.paths.push({
path: jdd.generatePath(config, '[' + index + ']'),
line: config.line
});
config.currentPath.push('/[' + index + ']');
jdd.formatVal(arrayVal, config);
config.currentPath.pop();
});
jdd.finishArray(config);
config.currentPath.pop();
},
/**
* Generate the start of the an array in the output stream and push in the new path
*/
startArray: function (config) {
config.indent++;
config.out += '[';
if (config.paths.length === 0) {
/*
* Then we are at the top of the array and we want to add
* a path for it.
*/
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
}
if (config.indent === 0) {
config.indent++;
}
},
/**
* Finish the array, outdent, and pop off all the path
*/
finishArray: function (config) {
if (config.indent === 0) {
config.indent--;
}
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']';
if (config.indent !== 0) {
config.out += ',';
} else {
config.out += jdd.newLine(config);
}
},
/**
* Generate the start of the an object in the output stream and push in the new path
*/
startObject: function (config) {
config.indent++;
config.out += '{';
if (config.paths.length === 0) {
/*
* Then we are at the top of the object and we want to add
* a path for it.
*/
config.paths.push({
path: jdd.generatePath(config),
line: config.line
});
}
if (config.indent === 0) {
config.indent++;
}
},
/**
* Finish the object, outdent, and pop off all the path
*/
finishObject: function (config) {
if (config.indent === 0) {
config.indent--;
}
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + '}';
if (config.indent !== 0) {
config.out += ',';
} else {
config.out += jdd.newLine(config);
}
},
/**
* Format a specific value into the output stream.
*/
formatVal: function (val, config) {
if (getType(val) === 'array') {
config.out += '[';
config.indent++;
val.forEach(function (arrayVal, index) {
config.out += jdd.newLine(config) + jdd.getTabs(config.indent);
config.paths.push({
path: jdd.generatePath(config, '[' + index + ']'),
line: config.line
});
config.currentPath.push('/[' + index + ']');
jdd.formatVal(arrayVal, config);
config.currentPath.pop();
});
jdd.removeTrailingComma(config);
config.indent--;
config.out += jdd.newLine(config) + jdd.getTabs(config.indent) + ']' + ',';
} else if (getType(val) === 'object') {
jdd.formatAndDecorate(config, val);
} else if (getType(val) === 'string') {
config.out += '"' + jdd.unescapeString(val) + '",';
} else if (getType(val) === 'number') {
config.out += val + ',';
} else if (getType(val) === 'boolean') {
config.out += val + ',';
} else if (getType(val) === 'null') {
config.out += 'null,';
}
},
/**
* When we parse the JSON string we end up removing the escape strings when we parse it
* into objects. This results in invalid JSON if we insert those strings back into the
* generated JSON. We also need to look out for characters that change the line count
* like new lines and carriage returns.
*
* This function puts those escaped values back when we generate the JSON output for the
* well known escape strings in JSON. It handles properties and values.
*
* This function does not handle unicode escapes. Unicode escapes are optional in JSON
* and the JSON output is still valid with a unicode character in it.
*/
unescapeString: function (val) {
if (val) {
return val.replace('\\', '\\\\') // Single slashes need to be replaced first
.replace(/\"/g, '\\"') // Then double quotes
.replace(/\n/g, '\\n') // New lines
.replace('\b', '\\b') // Backspace
.replace(/\f/g, '\\f') // Formfeed
.replace(/\r/g, '\\r') // Carriage return
.replace(/\t/g, '\\t'); // Horizontal tabs
} else {
return val;
}
},
/**
* Generate a JSON path based on the specific configuration and an optional property.
*/
generatePath: function (config, prop) {
var s = '';
config.currentPath.forEach(function (path) {
s += path;
});
if (prop) {
s += '/' + prop;
}
if (s.length === 0) {
return '/';
} else {
return s;
}
},
/**
* Add a new line to the output stream
*/
newLine: function (config) {
config.line++;
return '\n';
},
/**
* Sort all the relevant properties and return them in an alphabetical sort by property key
*/
getSortedProperties: function (/*Object*/ obj) {
var props = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
props.push(prop);
}
}
props = props.sort(function (a, b) {
return a.localeCompare(b);
});
return props;
},
/**
* Generate the diff and verify that it matches a JSON path
*/
generateDiff: function (config1, path1, config2, path2, /*String*/ msg, type) {
if (path1 !== '/' && path1.charAt(path1.length - 1) === '/') {
path1 = path1.substring(0, path1.length - 1);
}
if (path2 !== '/' && path2.charAt(path2.length - 1) === '/') {
path2 = path2.substring(0, path2.length - 1);
}
var pathObj1 = config1.paths.find(function (path) {
return path.path === path1;
});
var pathObj2 = config2.paths.find(function (path) {
return path.path === path2;
});
if (!pathObj1) {
throw 'Unable to find line number for (' + msg + '): ' + path1;
}
if (!pathObj2) {
throw 'Unable to find line number for (' + msg + '): ' + path2;
}
return {
path1: pathObj1,
path2: pathObj2,
type: type,
msg: msg
};
},
/**
* Get the current indent level
*/
getTabs: function (/*int*/ indent) {
var s = '';
for (var i = 0; i < indent; i++) {
s += ' ';
}
return s;
},
/**
* Remove the trailing comma from the output.
*/
removeTrailingComma: function (config) {
/*
* Remove the trailing comma
*/
if (config.out.charAt(config.out.length - 1) === ',') {
config.out = config.out.substring(0, config.out.length - 1);
}
},
/**
* Create a config object for holding differences
*/
createConfig: function () {
return {
out: '',
indent: -1,
currentPath: [],
paths: [],
line: 1
};
},
/**
* Format the output pre tags.
*/
formatPRETags: function () {
forEach($('pre'), function (pre) {
var codeBlock = $('<pre class="codeBlock"></pre>');
var lineNumbers = $('<div class="gutter"></div>');
codeBlock.append(lineNumbers);
var codeLines = $('<div></div>');
codeBlock.append(codeLines);
var addLine = function (line, index) {
var div = $('<div class="codeLine line' + (index + 1) + '"></div>');
lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
var span = $('<span class="code"></span');
span.text(line);
div.append(span);
codeLines.append(div);
};
var lines = $(pre).text().split('\n');
lines.forEach(addLine);
codeBlock.addClass($(pre).attr('class'));
codeBlock.attr('id', $(pre).attr('id'));
$(pre).replaceWith(codeBlock);
});
},
/**
* Format the text edits which handle the JSON input
*/
formatTextAreas: function () {
forEach($('textarea'), function (textarea) {
var codeBlock = $('<div class="codeBlock"></div>');
var lineNumbers = $('<div class="gutter"></div>');
codeBlock.append(lineNumbers);
var addLine = function (line, index) {
lineNumbers.append($('<span class="line-number">' + (index + 1) + '.</span>'));
};
var lines = $(textarea).val().split('\n');
lines.forEach(addLine);
$(textarea).replaceWith(codeBlock);
codeBlock.append(textarea);
});
},
handleDiffClick: function (line, side) {
var diffs = jdd.diffs.filter(function (diff) {
if (side === jdd.LEFT) {
return line === diff.path1.line;
} else if (side === jdd.RIGHT) {
return line === diff.path2.line;
} else {
return line === diff.path1.line || line === diff.path2.line;
}
});
$('pre.left span.code').removeClass('selected');
$('pre.right span.code').removeClass('selected');
$('ul.toolbar').text('');
diffs.forEach(function (diff) {
$('pre.left div.line' + diff.path1.line + ' span.code').addClass('selected');
$('pre.right div.line' + diff.path2.line + ' span.code').addClass('selected');
});
if (side === jdd.LEFT || side === jdd.RIGHT) {
jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
return diff.path1.line === line;
});
}
if (jdd.currentDiff === -1) {
jdd.currentDiff = jdd.diffs.findIndex(function (diff) {
return diff.path2.line === line;
});
}
var buttons = $('<div id="buttons"><div>');
var prev = $('<a href="#" title="Previous difference" id="prevButton">&lt;</a>');
prev.addClass('disabled');
prev.click(function (e) {
e.preventDefault();
jdd.highlightPrevDiff();
});
buttons.append(prev);
buttons.append('<span id="prevNextLabel"></span>');
var next = $('<a href="#" title="Next difference" id="nextButton">&gt;</a>');
next.click(function (e) {
e.preventDefault();
jdd.highlightNextDiff();
});
buttons.append(next);
$('ul.toolbar').append(buttons);
jdd.updateButtonStyles();
jdd.showDiffDetails(diffs);
},
highlightPrevDiff: function () {
if (jdd.currentDiff > 0) {
jdd.currentDiff--;
jdd.highlightDiff(jdd.currentDiff);
jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
jdd.updateButtonStyles();
}
},
highlightNextDiff: function () {
if (jdd.currentDiff < jdd.diffs.length - 1) {
jdd.currentDiff++;
jdd.highlightDiff(jdd.currentDiff);
jdd.scrollToDiff(jdd.diffs[jdd.currentDiff]);
jdd.updateButtonStyles();
}
},
updateButtonStyles: function () {
$('#prevButton').removeClass('disabled');
$('#nextButton').removeClass('disabled');
$('#prevNextLabel').text((jdd.currentDiff + 1) + ' of ' + (jdd.diffs.length));
if (jdd.currentDiff === 1) {
$('#prevButton').addClass('disabled');
} else if (jdd.currentDiff === jdd.diffs.length - 1) {
$('#nextButton').addClass('disabled');
}
},
/**
* Highlight the diff at the specified index
*/
highlightDiff: function (index) {
jdd.handleDiffClick(jdd.diffs[index].path1.line, jdd.BOTH);
},
/**
* Show the details of the specified diff
*/
showDiffDetails: function (diffs) {
diffs.forEach(function (diff) {
var li = $('<li></li>');
li.html(diff.msg);
$('ul.toolbar').append(li);
li.click(function () {
jdd.scrollToDiff(diff);
});
});
},
/**
* Scroll the specified diff to be visible
*/
scrollToDiff: function (diff) {
$('html, body').animate({
scrollTop: $('pre.left div.line' + diff.path1.line + ' span.code').offset().top
}, 0);
},
/**
* Process the specified diff
*/
processDiffs: function () {
var left = [];
var right = [];
jdd.diffs.forEach(function (diff) {
$('pre.left div.line' + diff.path1.line + ' span.code').addClass(diff.type).addClass('diff');
if (left.indexOf(diff.path1.line) === -1) {
$('pre.left div.line' + diff.path1.line + ' span.code').click(function () {
jdd.handleDiffClick(diff.path1.line, jdd.LEFT);
});
left.push(diff.path1.line);
}
$('pre.right div.line' + diff.path2.line + ' span.code').addClass(diff.type).addClass('diff');
if (right.indexOf(diff.path2.line) === -1) {
$('pre.right div.line' + diff.path2.line + ' span.code').click(function () {
jdd.handleDiffClick(diff.path2.line, jdd.RIGHT);
});
right.push(diff.path2.line);
}
});
jdd.diffs = jdd.diffs.sort(function (a, b) {
return a.path1.line - b.path1.line;
});
},
/**
* Validate the input against the JSON parser
*/
validateInput: function (json, side) {
try {
jsl.parser.parse(json);
if (side === jdd.LEFT) {
$('#errorLeft').text('').hide();
$('#textarealeft').removeClass('error');
} else {
$('#errorRight').text('').hide();
$('#textarearight').removeClass('error');
}
return true;
} catch (parseException) {
if (side === jdd.LEFT) {
$('#errorLeft').text(parseException.message).show();
$('#textarealeft').addClass('error');
} else {
$('#errorRight').text(parseException.message).show();
$('#textarearight').addClass('error');
}
return false;
}
},
/**
* Handle the file uploads
*/
handleFiles: function (files, side) {
var reader = new FileReader();
reader.onload = (function () {
return function (e) {
if (side === jdd.LEFT) {
$('#textarealeft').val(e.target.result);
} else {
$('#textarearight').val(e.target.result);
}
};
})(files[0]);
reader.readAsText(files[0]);
},
setupNewDiff: function () {
$('div.initContainer').show();
$('div.diffcontainer').hide();
$('div.diffcontainer pre').text('');
$('ul.toolbar').text('');
},
/**
* Generate the report section with the diff
*/
generateReport: function () {
var report = $('#report');
report.text('');
var newDiff = $('<button>Perform a new diff</button>');
report.append(newDiff);
newDiff.click(function () {
jdd.setupNewDiff();
});
if (jdd.diffs.length === 0) {
report.append('<span>The two files were semantically identical.</span>');
return;
}
var typeCount = 0;
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
} else if (diff.type === jdd.TYPE) {
typeCount++;
}
});
var title = $('<div class="reportTitle"></div>');
if (jdd.diffs.length === 1) {
title.text('Found ' + (jdd.diffs.length) + ' difference');
} else {
title.text('Found ' + (jdd.diffs.length) + ' differences');
}
report.prepend(title);
var filterBlock = $('<span class="filterBlock">Show:</span>');
/*
* The missing checkbox
*/
if (missingCount > 0) {
var missing = $('<label><input id="showMissing" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (missingCount === 1) {
missing.append(missingCount + ' missing property');
} else {
missing.append(missingCount + ' missing properties');
}
missing.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.missing').addClass('missing_off').removeClass('missing');
} else {
$('span.code.diff.missing_off').addClass('missing').removeClass('missing_off');
}
});
filterBlock.append(missing);
}
/*
* The types checkbox
*/
if (typeCount > 0) {
var types = $('<label><input id="showTypes" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (typeCount === 1) {
types.append(typeCount + ' incorrect type');
} else {
types.append(typeCount + ' incorrect types');
}
types.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.type').addClass('type_off').removeClass('type');
} else {
$('span.code.diff.type_off').addClass('type').removeClass('type_off');
}
});
filterBlock.append(types);
}
/*
* The equals checkbox
*/
if (eqCount > 0) {
var eq = $('<label><input id="showEq" type="checkbox" name="checkbox" value="value" checked="true"></label>');
if (eqCount === 1) {
eq.append(eqCount + ' unequal value');
} else {
eq.append(eqCount + ' unequal values');
}
eq.children('input').click(function () {
if (!$(this).prop('checked')) {
$('span.code.diff.eq').addClass('eq_off').removeClass('eq');
} else {
$('span.code.diff.eq_off').addClass('eq').removeClass('eq_off');
}
});
filterBlock.append(eq);
}
report.append(filterBlock);
},
/**
* Implement the compare button and complete the compare process
*/
compare: function () {
if (jdd.requestCount !== 0) {
/*
* This means we have a pending request and we just need to wait for that to finish.
*/
return;
}
$('body').addClass('progress');
$('#compare').prop('disabled', true);
var loadUrl = function (id, errId) {
if ($('#' + id).val().trim().substring(0, 4).toLowerCase() === 'http') {
jdd.requestCount++;
$.post('proxy.php',
{
'url': $('#' + id).val().trim()
}, function (responseObj) {
if (responseObj.error) {
$('#' + errId).text(responseObj.result).show();
$('#' + id).addClass('error');
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
} else {
$('#' + id).val(responseObj.content);
jdd.requestCount--;
jdd.compare();
}
}, 'json');
return true;
} else {
return false;
}
};
if (loadUrl('textarealeft', 'errorLeft')) {
return;
}
if (loadUrl('textarearight', 'errorRight')) {
return;
}
/*
* We'll start by running the text through JSONlint since it gives
* much better error messages.
*/
var leftValid = jdd.validateInput($('#textarealeft').val(), jdd.LEFT);
var rightValid = jdd.validateInput($('#textarearight').val(), jdd.RIGHT);
if (!leftValid || !rightValid) {
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
return;
}
$('div.initContainer').hide();
$('div.diffcontainer').show();
jdd.diffs = [];
var left = JSON.parse($('#textarealeft').val());
var right = JSON.parse($('#textarearight').val());
var config = jdd.createConfig();
jdd.formatAndDecorate(config, left);
$('#out').text(config.out);
var config2 = jdd.createConfig();
jdd.formatAndDecorate(config2, right);
$('#out2').text(config2.out);
jdd.formatPRETags();
config.currentPath = [];
config2.currentPath = [];
jdd.diffVal(left, config, right, config2);
jdd.processDiffs();
jdd.generateReport();
//console.log('diffs: ' + JSON.stringify(jdd.diffs));
if (jdd.diffs.length > 0) {
jdd.highlightDiff(0);
jdd.currentDiff = 0;
jdd.updateButtonStyles();
}
$('body').removeClass('progress');
$('#compare').prop('disabled', false);
/*
* We want to switch the toolbar bar between fixed and absolute position when you
* scroll so you can get the maximum number of toolbar items.
*/
var toolbarTop = $('#toolbar').offset().top - 15;
$(window).scroll(function () {
if (toolbarTop < $(window).scrollTop()) {
$('#toolbar').css('position', 'fixed').css('top', '10px');
} else {
$('#toolbar').css('position', 'absolute').css('top', '');
}
});
},
/**
* Load in the sample data
*/
loadSampleData: function () {
$('#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}');
/*$('#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"}]');
$('#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"}]}');*/
$('#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"]}');
},
getParameterByName: function (name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
};
jQuery(document).ready(function () {
$('#compare').click(function () {
jdd.compare();
});
if (jdd.getParameterByName('left')) {
$('#textarealeft').val(jdd.getParameterByName('left'));
}
if (jdd.getParameterByName('right')) {
$('#textarearight').val(jdd.getParameterByName('right'));
}
if (jdd.getParameterByName('left') && jdd.getParameterByName('right')) {
jdd.compare();
}
$('#sample').click(function (e) {
e.preventDefault();
jdd.loadSampleData();
});
$(document).keydown(function (event) {
if (event.keyCode === 78 || event.keyCode === 39) {
/*
* The N key or right arrow key
*/
jdd.highlightNextDiff();
} else if (event.keyCode === 80 || event.keyCode === 37) {
/*
* The P key or left arrow key
*/
jdd.highlightPrevDiff();
}
});
});
// polyfills
// Array.prototype.find
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function (predicate) {
// 1. Let O be ? ToObject(this value).
if (this === null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
},
configurable: true,
writable: true
});
}
// Array.prototype.findIndex
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function (predicate) {
// 1. Let O be ? ToObject(this value).
if (this === null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return k.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return k;
}
// e. Increase k by 1.
k++;
}
// 7. Return -1.
return -1;
},
configurable: true,
writable: true
});
}
\ No newline at end of file
... ...
/*******************************************************************************
*
* Copyright 2015-2017 Zack Grossbart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
'use strict';
/**
* This is the main test harness for JSONDiff. It runs unit tests and integrations tests in the browser.
* To run these tests just access the index-test.html page. You can run the tests on the live site at
* http://www.jsondiff.com/index-test.html.
*
* Each test follows the same basic pattern: set the text of the left and right text areas then run the
* jdd.compare function. Once the compare completes we examine the JSON data in the jdd.diffs object to
* determine if the test completed successfully. As a last step we clear out the two text areas and call
* jdd.setupNewDiff to prepare for the next test.
*/
/**
* This is the basic compate test. It does a compare of our sample data and makes sure that we have the
* correct number of diffs of each type. It also checks a few random diffs to make sure they are correct.
*/
QUnit.test('Object compare tests', function (assert) {
$('#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}');
$('#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"]}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 20, 'Checking for the correct number of differences');
var typeCount = 0;
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
} else if (diff.type === jdd.TYPE) {
typeCount++;
}
});
assert.ok(eqCount === 4, 'Checking unequal values');
assert.ok(missingCount === 11, 'Checking missing values');
assert.ok(typeCount === 5, 'Checking incorrect types');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test focuses on finding differences between objects and arrays with similar data at the
* root of the JSON document.
*/
QUnit.test('Array to object compare tests', function (assert) {
$('#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"}]');
$('#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"}]}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test focuses on comparing arrays and makes sure that we can properly tell the difference
* between missing and unequal values.
*/
QUnit.test('Array compare tests', function (assert) {
$('#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"}]');
$('#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"}]');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 7, 'Checking for the correct number of differences');
var eqCount = 0;
var missingCount = 0;
jdd.diffs.forEach(function (diff) {
if (diff.type === jdd.EQUALITY) {
eqCount++;
} else if (diff.type === jdd.MISSING) {
missingCount++;
}
});
assert.ok(eqCount === 6, 'Checking unequal values');
assert.ok(missingCount === 1, 'Checking missing values');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test works a lot like the array to object compare tests, but it switches the left and right
* values. We've had a few bugs that only show up when the difference is on a specific side.
*/
QUnit.test('Object to array compare tests', function (assert) {
$('#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"}]}');
$('#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"}]');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 1, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking incorrect type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* These tests cover differences with whitespace in them. Specifically cases where there are new lines, backspaces,
* tabs, and other whitespace characters within the JSON keys and values. These values are escaped in the JSON source,
* but show up unescaped in the JSON object. We need to make sure we add them back and convert the line numbers properly.
*/
QUnit.test('Whitespace formatting tests', function (assert) {
$('#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"}');
$('#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"}');
jdd.compare();
// This test makes sure there wasn't a parsing error
assert.ok(jdd.diffs.length > 0, 'Checking for parsing errors');
assert.ok(jdd.diffs.length === 19, 'Checking for the correct number of differences');
assert.ok(jdd.diffs[0].type === jdd.MISSING, 'Checking missing property');
assert.ok(jdd.diffs[0].msg === 'Missing property <code>a\\bx</code> from the object on the left side', 'Checking property formatting');
assert.ok(jdd.diffs[9].type === jdd.MISSING, 'Checking missing property');
assert.ok(jdd.diffs[9].msg === 'Missing property <code>a\"b</code> from the object on the right side', 'Checking property formatting');
assert.ok(jdd.diffs[14].type === jdd.EQUALITY, 'Checking missing property');
assert.ok(jdd.diffs[18].type === jdd.EQUALITY, 'Checking missing property');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the right side of the compare.
*/
QUnit.test('Null array length tests - right side', function (assert) {
$('#textarealeft').val('{ "akey": [] }');
$('#textarearight').val('{ "akey": null }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the left side of the compare.
*/
QUnit.test('Null array length tests - left side', function (assert) {
$('#textarealeft').val('{ "akey": null }');
$('#textarearight').val('{ "akey": [] }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the right side of the compare.
*/
QUnit.test('Null object length tests - right side', function (assert) {
$('#textarealeft').val('{ "akey": {} }');
$('#textarearight').val('{ "akey": null }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
/**
* This test tests for null values when compared to arrays on the left side of the compare.
*/
QUnit.test('Null object length tests - left side', function (assert) {
$('#textarealeft').val('{ "akey": null }');
$('#textarearight').val('{ "akey": {} }');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.TYPE, 'Checking correct type');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
QUnit.test('Escaped quotes test', function (assert) {
$('#textarealeft').val('{"link": "<a href=\\\"http://google.com/\\\">Google</a>"}');
$('#textarearight').val('{"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"}');
jdd.compare();
assert.ok(jdd.diffs[0].type === jdd.EQUALITY, 'Checking correct type');
assert.ok($('pre.right div.line2 span').text().trim() === '"link": "<a href=\\\"http://googlex.com/\\\">Google</a>"', 'Checking escaped quotes');
assert.ok($('pre.left div.line2 span').text().trim() === '"link": "<a href=\\\"http://google.com/\\\">Google</a>"', 'Checking escaped quotes');
$('#textarealeft').val('');
$('#textarearight').val('');
jdd.setupNewDiff();
});
QUnit.done(function () {
$('div.initContainer').hide();
$('div.diffContainer').hide();
});
... ...
/*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 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;
/**
* 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).
*
**/
jsl.format = (function () {
function repeat(s, count) {
return new Array(count + 1).join(s);
}
function formatJson(json) {
var i = 0,
il = 0,
tab = " ",
newJson = "",
indentLevel = 0,
inString = false,
currentChar = null;
for (i = 0, il = json.length; i < il; i += 1) {
currentChar = json.charAt(i);
switch (currentChar) {
case '{':
case '[':
if (!inString) {
newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '}':
case ']':
if (!inString) {
indentLevel -= 1;
newJson += "\n" + repeat(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ',':
if (!inString) {
newJson += ",\n" + repeat(tab, indentLevel);
} else {
newJson += currentChar;
}
break;
case ':':
if (!inString) {
newJson += ": ";
} else {
newJson += currentChar;
}
break;
case ' ':
case "\n":
case "\t":
if (inString) {
newJson += currentChar;
}
break;
case '"':
if (i > 0 && json.charAt(i - 1) !== '\\') {
inString = !inString;
}
newJson += currentChar;
break;
default:
newJson += currentChar;
break;
}
}
return newJson;
}
return { "formatJson": formatJson };
}());
\ No newline at end of file
... ...
/*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 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;
/**
* Helper Function for Caret positioning
* Gratefully borrowed from the Masked Input Plugin by Josh Bush
* http://digitalbush.com/projects/masked-input-plugin
**/
$.fn.caret = function (begin, end) {
if (this.length === 0) {
return;
}
if (typeof begin === 'number') {
end = (typeof end === 'number') ? end : begin;
return this.each(function () {
if (this.setSelectionRange) {
this.focus();
this.setSelectionRange(begin, end);
} else if (this.createTextRange) {
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', begin);
range.select();
}
});
} else {
if (this[0].setSelectionRange) {
begin = this[0].selectionStart;
end = this[0].selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
begin = -range.duplicate().moveStart('character', -100000);
end = begin + range.text.length;
}
return {"begin": begin, "end": end};
}
};
/**
* jsl.interactions - provides support for interactions within JSON Lint.
*
**/
jsl.interactions = (function () {
var reformatParam,
reformat,
compress;
/******* UTILITY METHODS *******/
/**
* Get the Nth position of a character in a string
* @searchStr the string to search through
* @char the character to find
* @pos int the nth character to find, 1 based.
*
* @return int the position of the character found
**/
function getNthPos(searchStr, char, pos) {
var i,
charCount = 0,
strArr = searchStr.split(char);
if (pos === 0) {
return 0;
}
for (i = 0; i < pos; i++) {
if (i >= strArr.length) {
return -1;
}
// +1 because we split out some characters
charCount += strArr[i].length + char.length;
}
return charCount;
}
/**
* Get a URL parameter from the current windows URL.
* Courtesy Paul Oppenheim: http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
* @param name the parameter to retrieve
* @return string the url parameter's value, if any
**/
function getURLParameter(name) {
param = (new RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || ['', null])[1];
if (param) {
return decodeURIComponent(param);
} else {
return null;
}
}
/******* INTERACTION METHODS *******/
/**
* Validate the JSON we've been given, displaying an error or success message.
* @return void
**/
function validate() {
var lineNum,
lineMatches,
lineStart,
lineEnd,
jsonVal,
result;
jsonVal = $('#json_input').val();
try {
result = jsl.parser.parse(jsonVal);
if (result) {
$('#results').removeClass('error').addClass('success');
$('div.linedwrap').removeClass('redBorder').addClass('greenBorder');
$('#results').text('Valid JSON');
if (reformat) {
$('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, " "));
}
if (compress) {
$('#json_input').val(JSON.stringify(JSON.parse(jsonVal), null, ""));
}
} else {
alert("An unknown error occurred. Please contact Arc90.");
}
} catch (parseException) {
/**
* If we failed to validate, run our manual formatter and then re-validate so that we
* can get a better line number. On a successful validate, we don't want to run our
* manual formatter because the automatic one is faster and probably more reliable.
**/
try {
if (reformat) {
jsonVal = jsl.format.formatJson($('#json_input').val());
$('#json_input').val(jsonVal);
result = jsl.parser.parse($('#json_input').val());
}
} catch(e) {
parseException = e;
}
lineMatches = parseException.message.match(/line ([0-9]*)/);
if (lineMatches && typeof lineMatches === "object" && lineMatches.length > 1) {
lineNum = parseInt(lineMatches[1], 10);
if (lineNum === 1) {
lineStart = 0;
} else {
lineStart = getNthPos(jsonVal, "\n", lineNum - 1);
}
lineEnd = jsonVal.indexOf("\n", lineStart);
if (lineEnd < 0) {
lineEnd = jsonVal.length;
}
$('#json_input').focus().caret(lineStart, lineEnd);
}
$('#results').text(parseException.message);
$('#results').removeClass('success').addClass('error');
$('div.linedwrap').removeClass('greenBorder').addClass('redBorder');
}
$('#loadSpinner').hide();
}
/**
* Initialize variables, add event listeners, etc.
*
* @return void
**/
function init() {
reformatParam = getURLParameter('reformat');
reformat = reformatParam !== '0' && reformatParam !== 'no';
compress = reformatParam === 'compress',
jsonParam = getURLParameter('json');
if (compress) {
$('#headerText').html('JSONLint<span class="light">Compressor</span>');
}
if (!reformat) {
$('#headerText').html('JSONLint<span class="light">Lite</span>');
}
$('#validate').click(function () {
$('#results_header, #loadSpinner').show();
var jsonVal = $.trim($('#json_input').val());
if (jsonVal.substring(0, 4).toLowerCase() === "http") {
$.post("proxy.php", {"url": jsonVal}, function (responseObj) {
$('#json_input').val(responseObj.content);
validate();
}, 'json');
} else {
validate();
}
return false;
});
$('#json_input').keyup(function () {
$('div.linedwrap').removeClass('greenBorder').removeClass('redBorder');
}).linedtextarea({
selectedClass: 'lineselect'
}).focus();
$('#reset').click(function () {
$('#json_input').val('').focus();
});
$('#faqButton').click(function () {
$('#faq').slideToggle();
});
if (jsonParam) {
$('#json_input').val(jsonParam);
$('#validate').click();
}
}
return {
'init': init
};
}());
$(function () {
jsl.interactions.init();
});
... ...
var jsl = typeof(jsl) === 'undefined' ? {} : jsl;
/**
* JSON Lint Parser gratefully provided by Zach Carter
* https://github.com/zaach/jsonlint
**/
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}()
\ No newline at end of file
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
$url = filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
if (!$url || !preg_match("/^https?:/i", $url)) {
echo '{ "result": "Invalid URL. Please check your URL and try again.", "error": true }';
return;
}
$parsed_url = parse_url($url);
$scheme = strtolower($parsed_url['scheme']);
if (($scheme !== "http" && $scheme !== "https")) {
echo '{ "result": "Invalid URL - Please check your URL and try again.", "error": true }';
return;
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($data === false) {
echo '{ "result": "Unable to parse URL. Please check your URL and try again.", "error": true }';
return;
}
$contentLength = intval($info['download_content_length']);
$status = intval($info['http_code']);
if ($status >= 400) {
echo '{ "result": "URL returned bad status code ' . $status . '.", "error": true }';
return;
}
if ($contentLength >= 52428800) {
echo '{ "result": "URL content length greater than 10 megs (' . $contentLength . '). Validation not available for files this large.", "responseCode": "1" }';
return;
}
$response = new StdClass();
$response->status = $status;
$response->length = $contentLength;
$response->url = $info['url'];
$response->content = $data;
echo json_encode($response);
?>
... ...
/*!
* QUnit 2.1.1
* https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2017-01-06T01:52Z
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header (excluding toolbar) */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699A4;
background-color: #0D3349;
font-size: 1.5em;
line-height: 1em;
font-weight: 400;
border-radius: 5px 5px 0 0;
}
#qunit-header a {
text-decoration: none;
color: #C2CCD1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #FFF;
}
#qunit-banner {
height: 5px;
}
#qunit-filteredTest {
padding: 0.5em 1em 0.5em 1em;
color: #366097;
background-color: #F4FF77;
}
#qunit-userAgent {
padding: 0.5em 1em 0.5em 1em;
color: #FFF;
background-color: #2B81AF;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Toolbar */
#qunit-testrunner-toolbar {
padding: 0.5em 1em 0.5em 1em;
color: #5E740B;
background-color: #EEE;
}
#qunit-testrunner-toolbar .clearfix {
height: 0;
clear: both;
}
#qunit-testrunner-toolbar label {
display: inline-block;
}
#qunit-testrunner-toolbar input[type=checkbox],
#qunit-testrunner-toolbar input[type=radio] {
margin: 3px;
vertical-align: -2px;
}
#qunit-testrunner-toolbar input[type=text] {
box-sizing: border-box;
height: 1.6em;
}
.qunit-url-config,
.qunit-filter,
#qunit-modulefilter {
display: inline-block;
line-height: 2.1em;
}
.qunit-filter,
#qunit-modulefilter {
float: right;
position: relative;
margin-left: 1em;
}
.qunit-url-config label {
margin-right: 0.5em;
}
#qunit-modulefilter-search {
box-sizing: border-box;
width: 400px;
}
#qunit-modulefilter-search-container:after {
position: absolute;
right: 0.3em;
content: "\25bc";
color: black;
}
#qunit-modulefilter-dropdown {
/* align with #qunit-modulefilter-search */
box-sizing: border-box;
width: 400px;
position: absolute;
right: 0;
top: 50%;
margin-top: 0.8em;
border: 1px solid #D3D3D3;
border-top: none;
border-radius: 0 0 .25em .25em;
color: #000;
background-color: #F5F5F5;
z-index: 99;
}
#qunit-modulefilter-dropdown a {
color: inherit;
text-decoration: none;
}
#qunit-modulefilter-dropdown .clickable.checked {
font-weight: bold;
color: #000;
background-color: #D2E0E6;
}
#qunit-modulefilter-dropdown .clickable:hover {
color: #FFF;
background-color: #0D3349;
}
#qunit-modulefilter-actions {
display: block;
overflow: auto;
/* align with #qunit-modulefilter-dropdown-list */
font: smaller/1.5em sans-serif;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
box-sizing: border-box;
max-height: 2.8em;
display: block;
padding: 0.4em;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
float: right;
font: inherit;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
/* insert padding to align with checkbox margins */
padding-left: 3px;
}
#qunit-modulefilter-dropdown-list {
max-height: 200px;
overflow-y: auto;
margin: 0;
border-top: 2px groove threedhighlight;
padding: 0.4em 0 0;
font: smaller/1.5em sans-serif;
}
#qunit-modulefilter-dropdown-list li {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#qunit-modulefilter-dropdown-list .clickable {
display: block;
padding-left: 0.15em;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 1em 0.4em 1em;
border-bottom: 1px solid #FFF;
list-style-position: inside;
}
#qunit-tests > li {
display: none;
}
#qunit-tests li.running,
#qunit-tests li.pass,
#qunit-tests li.fail,
#qunit-tests li.skipped,
#qunit-tests li.aborted {
display: list-item;
}
#qunit-tests.hidepass {
position: relative;
}
#qunit-tests.hidepass li.running,
#qunit-tests.hidepass li.pass {
visibility: hidden;
position: absolute;
width: 0;
height: 0;
padding: 0;
border: 0;
margin: 0;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li.skipped strong {
cursor: default;
}
#qunit-tests li a {
padding: 0.5em;
color: #C2CCD1;
text-decoration: none;
}
#qunit-tests li p a {
padding: 0.25em;
color: #6B6464;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #FFF;
border-radius: 5px;
}
.qunit-source {
margin: 0.6em 0 0.3em;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: 0.2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 0.5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
color: #374E0C;
background-color: #E0F2BE;
text-decoration: none;
}
#qunit-tests ins {
color: #500;
background-color: #FFCACA;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: #000; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #FFF;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3C510C;
background-color: #FFF;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #FFF;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
}
#qunit-tests .fail { color: #000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: #008000; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/*** Aborted tests */
#qunit-tests .aborted { color: #000; background-color: orange; }
/*** Skipped tests */
#qunit-tests .skipped {
background-color: #EBECE9;
}
#qunit-tests .qunit-skipped-label {
background-color: #F4FF77;
display: inline-block;
font-style: normal;
color: #366097;
line-height: 1.8em;
padding: 0 0.5em;
margin: -0.4em 0.4em -0.4em 0;
}
/** Result */
#qunit-testresult {
color: #2B81AF;
background-color: #D2E0E6;
border-bottom: 1px solid #FFF;
}
#qunit-testresult .clearfix {
height: 0;
clear: both;
}
#qunit-testresult .module-name {
font-weight: 700;
}
#qunit-testresult-display {
padding: 0.5em 1em 0.5em 1em;
width: 85%;
float:left;
}
#qunit-testresult-controls {
padding: 0.5em 1em 0.5em 1em;
width: 10%;
float:left;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}
... ...
This diff could not be displayed because it is too large.
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
... ...
@-moz-keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
@-webkit-keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
@keyframes throbber-loader {
0% {
background: #dde2e7;
}
10% {
background: #6b9dc8;
}
40% {
background: #dde2e7;
}
}
/* :not(:required) hides these rules from IE9 and below */
.throbber-loader:not(:required) {
-moz-animation: throbber-loader 2000ms 300ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 300ms infinite ease-out;
animation: throbber-loader 2000ms 300ms infinite ease-out;
background: #dde2e7;
display: inline-block;
position: relative;
text-indent: -9999px;
width: 0.9em;
height: 1.5em;
margin: 0 1.6em;
}
.throbber-loader:not(:required):before, .throbber-loader:not(:required):after {
background: #dde2e7;
content: '\x200B';
display: inline-block;
width: 0.9em;
height: 1.5em;
position: absolute;
top: 0;
}
.throbber-loader:not(:required):before {
-moz-animation: throbber-loader 2000ms 150ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 150ms infinite ease-out;
animation: throbber-loader 2000ms 150ms infinite ease-out;
left: -1.6em;
}
.throbber-loader:not(:required):after {
-moz-animation: throbber-loader 2000ms 450ms infinite ease-out;
-webkit-animation: throbber-loader 2000ms 450ms infinite ease-out;
animation: throbber-loader 2000ms 450ms infinite ease-out;
right: -1.6em;
}
... ...
{"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"]}
\ No newline at end of file
... ...