From ad55893c17a115adbd05b1262d9634d272a69279 Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Sun, 26 Feb 2017 09:56:53 +0100 Subject: [PATCH 1/6] WIP: extract jsonp wrapper --- extension/src/json-viewer/check-if-json.js | 3 +- .../src/json-viewer/content-extractor.js | 27 ++++++++--- extension/src/json-viewer/extract-json.js | 45 ++++++++++++++++--- .../src/json-viewer/highlight-content.js | 17 +++---- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/extension/src/json-viewer/check-if-json.js b/extension/src/json-viewer/check-if-json.js index 67398665..32e7ef98 100644 --- a/extension/src/json-viewer/check-if-json.js +++ b/extension/src/json-viewer/check-if-json.js @@ -55,7 +55,8 @@ function isJSON(jsonStr) { } function isJSONP(jsonStr) { - return isJSON(extractJSON(jsonStr)); + debugger + return isJSON(extractJSON.replaceWrapper(jsonStr)); } function checkIfJson(sucessCallback, element) { diff --git a/extension/src/json-viewer/content-extractor.js b/extension/src/json-viewer/content-extractor.js index 90db387b..0e756082 100644 --- a/extension/src/json-viewer/content-extractor.js +++ b/extension/src/json-viewer/content-extractor.js @@ -15,11 +15,12 @@ var REPLACE_WRAP_REGEX = new RegExp( "\"" + WRAP_START + "(-?\\d+\\.?[\\deE]*)" + WRAP_END + "\"", "g" ); -function contentExtractor(pre, options) { +function getJSON (pre, options) { return new Promise(function(resolve, reject) { try { var rawJsonText = pre.textContent; - var jsonExtracted = extractJSON(rawJsonText); + var jsonpWrapper = extractJSON.getWrapper(rawJsonText); + var jsonExtracted = extractJSON.replaceWrapper(rawJsonText); var wrappedText = wrapNumbers(jsonExtracted); var jsonParsed = JSON.parse(wrappedText); @@ -29,16 +30,25 @@ function contentExtractor(pre, options) { var decodedJson = JSON.stringify(jsonParsed); decodedJson = decodedJson.replace(REPLACE_WRAP_REGEX, "$1"); - var jsonFormatted = normalize(jsonFormater(decodedJson, options.structure)); - var jsonText = normalize(rawJsonText).replace(normalize(jsonExtracted), jsonFormatted); - resolve({jsonText: jsonText, jsonExtracted: decodedJson}); - + resolve({jsonObj: decodedJson, jsonpWrapper: jsonpWrapper, options: options}); } catch(e) { reject(new Error('contentExtractor: ' + e.message)); } }); } +function formatJSON (data) { + return new Promise(function(resolve, reject) { + var jsonFormatted = normalize(jsonFormater(data.jsonObj, data.options.structure)); + var jsonText = normalize(data.jsonpWrapper.header) + jsonFormatted + normalize(data.jsonpWrapper.footer); + resolve({jsonText: jsonText, jsonObj: data.jsonObj}); + }) +} + +// function filterJSON () { +// +// } + function normalize(json) { return json.replace(/\$/g, '$$$$'); } @@ -131,4 +141,7 @@ function isCharInString(char, previous) { char != '-'; } -module.exports = contentExtractor; +module.exports = { + getJSON: getJSON, + formatJSON: formatJSON +}; diff --git a/extension/src/json-viewer/extract-json.js b/extension/src/json-viewer/extract-json.js index 58200399..74cac90e 100644 --- a/extension/src/json-viewer/extract-json.js +++ b/extension/src/json-viewer/extract-json.js @@ -1,9 +1,42 @@ -function extractJSON(rawJson) { - return rawJson - .replace(/\s*while\((1|true)\)\s*;?/, '') - .replace(/\s*for\(;;\)\s*;?/, '') +var JSONP_HEADER_REGEX = [ + /\s*while\((1|true)\)\s*;?/, + /\s*for\(;;\)\s*;?/ +] +var JSONP_FOOTER_REGEX = /\}\);?\s*$/ + +function replaceWrapper(rawJSON) { + return rawJSON + .replace(JSONP_HEADER_REGEX[0], '') + .replace(JSONP_HEADER_REGEX[1], '') .replace(/^[^{\[].+\({/, '{') - .replace(/}\);?\s*$/, '}'); + .replace(JSONP_FOOTER_REGEX, '}'); +} + +function getWrapper (rawJSON) { + var wrapper = { header: '', footer: '' }; + + var a = rawJSON.match(JSONP_HEADER_REGEX[0]); + if (a && a.length) { + wrapper.header = a[0]; + } + a = rawJSON.match(JSONP_HEADER_REGEX[1]); + if (a && a.length) { + wrapper.header = a[0]; + } + a = rawJSON.match(/^[^{\[].+\({/); + if (a && a.length) { + wrapper.header = a[0]; + } + + b = rawJSON.match(JSONP_FOOTER_REGEX); + if (b && b.length) { + wrapper.footer = b[0]; + } + + return wrapper; } -module.exports = extractJSON; +module.exports = { + replaceWrapper: replaceWrapper, + getWrapper: getWrapper +}; diff --git a/extension/src/json-viewer/highlight-content.js b/extension/src/json-viewer/highlight-content.js index 48f53666..293e706f 100644 --- a/extension/src/json-viewer/highlight-content.js +++ b/extension/src/json-viewer/highlight-content.js @@ -52,13 +52,14 @@ function highlightContent(pre, outsideViewer) { return pre.hidden = false; } - return contentExtractor(pre, options). - then(function(value) { - return loadRequiredCss(options).then(function() { return value; }); - }). - then(function(value) { - - var formatted = prependHeader(options, outsideViewer, value.jsonText); + return contentExtractor.getJSON(pre, options) + .then(contentExtractor.formatJSON) + .then(function(data) { + return loadRequiredCss(options).then(function() { return data; }); + }) + .then(function(data) { + + var formatted = prependHeader(options, outsideViewer, data.jsonText); var highlighter = new Highlighter(formatted, options); if (options.addons.autoHighlight) { @@ -83,7 +84,7 @@ function highlightContent(pre, outsideViewer) { highlighter.fold(); } - exposeJson(value.jsonExtracted, outsideViewer); + exposeJson(data.jsonObj, outsideViewer); renderExtras(pre, options, highlighter); }); From ea84a0428605d28bbbfbc3a5c7addcc32be4ec2b Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Sun, 26 Feb 2017 22:40:14 +0100 Subject: [PATCH 2/6] Implement query parsing and filtering, should be refactored --- extension/src/json-viewer/check-if-json.js | 1 - .../src/json-viewer/content-extractor.js | 79 ++++++++++++++++--- .../src/json-viewer/highlight-content.js | 7 +- .../src/json-viewer/viewer/expose-json.js | 6 +- 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/extension/src/json-viewer/check-if-json.js b/extension/src/json-viewer/check-if-json.js index 32e7ef98..d9c009a4 100644 --- a/extension/src/json-viewer/check-if-json.js +++ b/extension/src/json-viewer/check-if-json.js @@ -55,7 +55,6 @@ function isJSON(jsonStr) { } function isJSONP(jsonStr) { - debugger return isJSON(extractJSON.replaceWrapper(jsonStr)); } diff --git a/extension/src/json-viewer/content-extractor.js b/extension/src/json-viewer/content-extractor.js index 0e756082..2f3f62d1 100644 --- a/extension/src/json-viewer/content-extractor.js +++ b/extension/src/json-viewer/content-extractor.js @@ -15,7 +15,7 @@ var REPLACE_WRAP_REGEX = new RegExp( "\"" + WRAP_START + "(-?\\d+\\.?[\\deE]*)" + WRAP_END + "\"", "g" ); -function getJSON (pre, options) { +function getJSON(pre, options) { return new Promise(function(resolve, reject) { try { var rawJsonText = pre.textContent; @@ -26,28 +26,43 @@ function getJSON (pre, options) { var jsonParsed = JSON.parse(wrappedText); if (options.addons.sortKeys) jsonParsed = sortByKeys(jsonParsed); - // Validate and decode json - var decodedJson = JSON.stringify(jsonParsed); - decodedJson = decodedJson.replace(REPLACE_WRAP_REGEX, "$1"); - - resolve({jsonObj: decodedJson, jsonpWrapper: jsonpWrapper, options: options}); + resolve({jsonObj: jsonParsed, jsonpWrapper: jsonpWrapper, options: options}); } catch(e) { reject(new Error('contentExtractor: ' + e.message)); } }); } -function formatJSON (data) { +function formatJSON(data) { return new Promise(function(resolve, reject) { - var jsonFormatted = normalize(jsonFormater(data.jsonObj, data.options.structure)); + var decodedJson = JSON.stringify(data.jsonObj).replace(REPLACE_WRAP_REGEX, "$1"); + var jsonFormatted = normalize(jsonFormater(decodedJson, data.options.structure)); var jsonText = normalize(data.jsonpWrapper.header) + jsonFormatted + normalize(data.jsonpWrapper.footer); + resolve({jsonText: jsonText, jsonObj: data.jsonObj}); }) } -// function filterJSON () { -// -// } +function filterJSON(query) { + return function (data) { + if (!query) { + return data + } + + var fullQuery = query.toString().split('.') + var filteredJson = null + if (Array.isArray(data.jsonObj)) { + filteredJson = data.jsonObj.reduce(function(acc, next) { + acc.push(applyFilter(next, fullQuery)) + return acc; + }, []) + } else { + filteredJson = applyFilter(data.jsonObj, fullQuery) + } + + return {jsonObj: filteredJson, jsonpWrapper: data.jsonpWrapper, options: data.options} + } +} function normalize(json) { return json.replace(/\$/g, '$$$$'); @@ -141,7 +156,47 @@ function isCharInString(char, previous) { char != '-'; } +function applyFilter(input, query) { + var currentKey = query.length ? query[0] : null + var nextQuery = query.slice(1) + + if (!currentKey) return input + + var index = null + var match = currentKey.match(/^(.*)\[([0-9]*)\]$/) + if (match && match.length > 0) { + currentKey = match[1] + index = parseInt(match[2], 10) + } + + if (typeof input[currentKey] !== 'object') { + var result = {} + result[currentKey] = input[currentKey] + return result + } + + if (Array.isArray(input[currentKey])) { + + return input[currentKey].reduce((acc, next, i) => { + if (index && index !== i) { + return acc + } + + if (typeof next === 'object') { + acc.push(applyFilter(next, nextQuery)) + } else { + acc.push(next) + } + + return acc + }, []) + } else { + return applyFilter(input[currentKey], nextQuery) + } +} + module.exports = { getJSON: getJSON, - formatJSON: formatJSON + formatJSON: formatJSON, + filterJSON: filterJSON }; diff --git a/extension/src/json-viewer/highlight-content.js b/extension/src/json-viewer/highlight-content.js index 293e706f..6d4c6538 100644 --- a/extension/src/json-viewer/highlight-content.js +++ b/extension/src/json-viewer/highlight-content.js @@ -53,6 +53,11 @@ function highlightContent(pre, outsideViewer) { } return contentExtractor.getJSON(pre, options) + .then((data) => { + exposeJson(data.jsonObj, outsideViewer); + return data; + }) + .then(contentExtractor.filterJSON(decodeURIComponent(document.location.hash.substring(1)))) .then(contentExtractor.formatJSON) .then(function(data) { return loadRequiredCss(options).then(function() { return data; }); @@ -84,9 +89,7 @@ function highlightContent(pre, outsideViewer) { highlighter.fold(); } - exposeJson(data.jsonObj, outsideViewer); renderExtras(pre, options, highlighter); - }); }).catch(function(e) { diff --git a/extension/src/json-viewer/viewer/expose-json.js b/extension/src/json-viewer/viewer/expose-json.js index 713c8383..92abbaea 100644 --- a/extension/src/json-viewer/viewer/expose-json.js +++ b/extension/src/json-viewer/viewer/expose-json.js @@ -1,12 +1,12 @@ -function exposeJson(text, outsideViewer) { +function exposeJson(jsonObj, outsideViewer) { console.info("[JSONViewer] Your json was stored into 'window.json', enjoy!"); if (outsideViewer) { - window.json = JSON.parse(text); + window.json = jsonObj; } else { var script = document.createElement("script") ; - script.innerHTML = 'window.json = ' + text + ';'; + script.innerHTML = 'window.json = ' + JSON.stringify(jsonObj) + ';'; document.head.appendChild(script); } } From ff192deeeb5b82a373b087cf6af1d8eadc0b28c7 Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Mon, 6 Mar 2017 22:59:04 +0100 Subject: [PATCH 3/6] Add support for secondaryFilter --- .../src/json-viewer/content-extractor.js | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/extension/src/json-viewer/content-extractor.js b/extension/src/json-viewer/content-extractor.js index 2f3f62d1..3cb9b929 100644 --- a/extension/src/json-viewer/content-extractor.js +++ b/extension/src/json-viewer/content-extractor.js @@ -157,42 +157,44 @@ function isCharInString(char, previous) { } function applyFilter(input, query) { - var currentKey = query.length ? query[0] : null + var querySegment = query.length ? query[0] : null var nextQuery = query.slice(1) - if (!currentKey) return input + if (!querySegment) return input - var index = null - var match = currentKey.match(/^(.*)\[([0-9]*)\]$/) + var secondaryFilter = [] + var match = querySegment.match(/^(.*)\[(.*)\]$/) if (match && match.length > 0) { - currentKey = match[1] - index = parseInt(match[2], 10) + querySegment = match[1] + secondaryFilter = (match[2] || '').split(',') } - if (typeof input[currentKey] !== 'object') { - var result = {} - result[currentKey] = input[currentKey] + var result = {} + if (!querySegment || typeof input[querySegment] !== 'object') { + if (secondaryFilter.length) { + secondaryFilter.forEach(function(key) { + if (input.hasOwnProperty(key)) { + result[key] = input[key] + } + }) + } else { + result[querySegment] = input[querySegment] + } return result } - if (Array.isArray(input[currentKey])) { - - return input[currentKey].reduce((acc, next, i) => { - if (index && index !== i) { - return acc - } - - if (typeof next === 'object') { + if (Array.isArray(input[querySegment])) { + result[querySegment] = input[querySegment].reduce((acc, next, i) => { + var indexInSecondaryFilter = secondaryFilter.indexOf(i.toString()) !== -1 + if (!secondaryFilter.length || (secondaryFilter.length && indexInSecondaryFilter)) { acc.push(applyFilter(next, nextQuery)) - } else { - acc.push(next) } - return acc }, []) } else { - return applyFilter(input[currentKey], nextQuery) + result[querySegment] = applyFilter(input[querySegment], nextQuery) } + return result } module.exports = { From 8636ffec17355a7f969fac0c3dc33c58f805af1e Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Sun, 19 Mar 2017 10:29:09 +0100 Subject: [PATCH 4/6] refactor extract-json --- extension/src/json-viewer/extract-json.js | 44 +++++++++-------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/extension/src/json-viewer/extract-json.js b/extension/src/json-viewer/extract-json.js index 74cac90e..f399dbf8 100644 --- a/extension/src/json-viewer/extract-json.js +++ b/extension/src/json-viewer/extract-json.js @@ -1,37 +1,25 @@ -var JSONP_HEADER_REGEX = [ - /\s*while\((1|true)\)\s*;?/, - /\s*for\(;;\)\s*;?/ -] -var JSONP_FOOTER_REGEX = /\}\);?\s*$/ +var JSONP_HEADER_REGEX = new RegExp([ + '(\\s*while\\((1|true)\\)\\s*;?)', + '(\\s*for\\(;;\\)\\s*;?)', + '(^[^{\\[].+?\\()' +].join('|')) -function replaceWrapper(rawJSON) { - return rawJSON - .replace(JSONP_HEADER_REGEX[0], '') - .replace(JSONP_HEADER_REGEX[1], '') - .replace(/^[^{\[].+\({/, '{') - .replace(JSONP_FOOTER_REGEX, '}'); +var JSONP_FOOTER_REGEX = /\);?\s*$/ + +function replaceWrapper(rawJsonString) { + return rawJsonString + .replace(JSONP_HEADER_REGEX, '') + .replace(JSONP_FOOTER_REGEX, '') } -function getWrapper (rawJSON) { +function getWrapper (rawJsonString) { var wrapper = { header: '', footer: '' }; - var a = rawJSON.match(JSONP_HEADER_REGEX[0]); - if (a && a.length) { - wrapper.header = a[0]; - } - a = rawJSON.match(JSONP_HEADER_REGEX[1]); - if (a && a.length) { - wrapper.header = a[0]; - } - a = rawJSON.match(/^[^{\[].+\({/); - if (a && a.length) { - wrapper.header = a[0]; - } + var header = rawJsonString.match(JSONP_HEADER_REGEX) + wrapper.header = header ? header[0] : '' - b = rawJSON.match(JSONP_FOOTER_REGEX); - if (b && b.length) { - wrapper.footer = b[0]; - } + var footer = rawJsonString.match(JSONP_FOOTER_REGEX) + wrapper.footer = footer ? footer[0] : '' return wrapper; } From bfbaee3f900870fdae6bfe73a641077be22b2bc3 Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Sun, 19 Mar 2017 11:20:42 +0100 Subject: [PATCH 5/6] Display the applied filter as prefix string in the result output --- extension/src/json-viewer/content-extractor.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extension/src/json-viewer/content-extractor.js b/extension/src/json-viewer/content-extractor.js index 3cb9b929..b612d82c 100644 --- a/extension/src/json-viewer/content-extractor.js +++ b/extension/src/json-viewer/content-extractor.js @@ -37,7 +37,14 @@ function formatJSON(data) { return new Promise(function(resolve, reject) { var decodedJson = JSON.stringify(data.jsonObj).replace(REPLACE_WRAP_REGEX, "$1"); var jsonFormatted = normalize(jsonFormater(decodedJson, data.options.structure)); - var jsonText = normalize(data.jsonpWrapper.header) + jsonFormatted + normalize(data.jsonpWrapper.footer); + var filterQuery = data.filterQuery + ? '/* FILTER: ' + data.filterQuery + ' */\n' + : '' + var jsonText = + filterQuery + + normalize(data.jsonpWrapper.header) + + jsonFormatted + + normalize(data.jsonpWrapper.footer); resolve({jsonText: jsonText, jsonObj: data.jsonObj}); }) From 3343ad4ec25ec0d2e58573a5eafd2166ca1c3d63 Mon Sep 17 00:00:00 2001 From: Nikola Pejoski Date: Sun, 19 Mar 2017 11:21:11 +0100 Subject: [PATCH 6/6] Move the json filter logic into separate file --- .../src/json-viewer/content-extractor.js | 65 +-------------- extension/src/json-viewer/filter.js | 79 +++++++++++++++++++ .../src/json-viewer/highlight-content.js | 5 +- 3 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 extension/src/json-viewer/filter.js diff --git a/extension/src/json-viewer/content-extractor.js b/extension/src/json-viewer/content-extractor.js index b612d82c..d061c9d2 100644 --- a/extension/src/json-viewer/content-extractor.js +++ b/extension/src/json-viewer/content-extractor.js @@ -50,27 +50,6 @@ function formatJSON(data) { }) } -function filterJSON(query) { - return function (data) { - if (!query) { - return data - } - - var fullQuery = query.toString().split('.') - var filteredJson = null - if (Array.isArray(data.jsonObj)) { - filteredJson = data.jsonObj.reduce(function(acc, next) { - acc.push(applyFilter(next, fullQuery)) - return acc; - }, []) - } else { - filteredJson = applyFilter(data.jsonObj, fullQuery) - } - - return {jsonObj: filteredJson, jsonpWrapper: data.jsonpWrapper, options: data.options} - } -} - function normalize(json) { return json.replace(/\$/g, '$$$$'); } @@ -163,49 +142,7 @@ function isCharInString(char, previous) { char != '-'; } -function applyFilter(input, query) { - var querySegment = query.length ? query[0] : null - var nextQuery = query.slice(1) - - if (!querySegment) return input - - var secondaryFilter = [] - var match = querySegment.match(/^(.*)\[(.*)\]$/) - if (match && match.length > 0) { - querySegment = match[1] - secondaryFilter = (match[2] || '').split(',') - } - - var result = {} - if (!querySegment || typeof input[querySegment] !== 'object') { - if (secondaryFilter.length) { - secondaryFilter.forEach(function(key) { - if (input.hasOwnProperty(key)) { - result[key] = input[key] - } - }) - } else { - result[querySegment] = input[querySegment] - } - return result - } - - if (Array.isArray(input[querySegment])) { - result[querySegment] = input[querySegment].reduce((acc, next, i) => { - var indexInSecondaryFilter = secondaryFilter.indexOf(i.toString()) !== -1 - if (!secondaryFilter.length || (secondaryFilter.length && indexInSecondaryFilter)) { - acc.push(applyFilter(next, nextQuery)) - } - return acc - }, []) - } else { - result[querySegment] = applyFilter(input[querySegment], nextQuery) - } - return result -} - module.exports = { getJSON: getJSON, - formatJSON: formatJSON, - filterJSON: filterJSON + formatJSON: formatJSON }; diff --git a/extension/src/json-viewer/filter.js b/extension/src/json-viewer/filter.js new file mode 100644 index 00000000..3522e818 --- /dev/null +++ b/extension/src/json-viewer/filter.js @@ -0,0 +1,79 @@ +function applyQuery(query) { + return function (data) { + var fullQuery = typeof query === 'string' + ? query.toString().split('.') + : ''; + + if (!query) { + return data; + } + + var filteredJson = null; + + if (Array.isArray(data.jsonObj)) { + filteredJson = data.jsonObj.reduce(function(acc, next) { + acc.push(filter(next, fullQuery)); + return acc; + }, []) + } else { + filteredJson = filter(data.jsonObj, fullQuery); + } + + return { + jsonObj: filteredJson, + jsonpWrapper: data.jsonpWrapper, + options: data.options, + filterQuery: query + } + } +} + +function filter(input, query) { + var querySegment = query.length ? query[0] : null; + var nextQuery = query.slice(1); + + if (!querySegment) return input; + + var secondaryFilter = []; + var match = querySegment.match(/^(.*)\[(.*)\]$/); + if (match && match.length > 0) { + querySegment = match[1]; + secondaryFilter = (match[2] || '') + .split(',') + .map(function (key) { + return key.trim() + }); + } + + var result = {}; + + if (!querySegment || typeof input[querySegment] !== 'object') { + if (secondaryFilter.length) { + secondaryFilter.forEach(function(key) { + if (input.hasOwnProperty(key)) { + result[key] = input[key]; + } + }) + } else { + result[querySegment] = input[querySegment]; + } + return result; + } + + if (Array.isArray(input[querySegment])) { + result[querySegment] = input[querySegment].reduce((acc, next, i) => { + var indexInSecondaryFilter = secondaryFilter.indexOf(i.toString()) !== -1; + if (!secondaryFilter.length || (secondaryFilter.length && indexInSecondaryFilter)) { + acc.push(filter(next, nextQuery)); + } + return acc; + }, []) + } else { + result[querySegment] = filter(input[querySegment], nextQuery); + } + return result; +} + +module.exports = { + applyQuery: applyQuery +} diff --git a/extension/src/json-viewer/highlight-content.js b/extension/src/json-viewer/highlight-content.js index 6d4c6538..8aeeda46 100644 --- a/extension/src/json-viewer/highlight-content.js +++ b/extension/src/json-viewer/highlight-content.js @@ -1,4 +1,5 @@ var contentExtractor = require('./content-extractor'); +var jsonFilter = require('./filter'); var Highlighter = require('./highlighter'); var timestamp = require('./timestamp'); var exposeJson = require('./viewer/expose-json'); @@ -52,12 +53,14 @@ function highlightContent(pre, outsideViewer) { return pre.hidden = false; } + var filterQuery = decodeURIComponent(document.location.hash.substring(1)) || '' + return contentExtractor.getJSON(pre, options) .then((data) => { exposeJson(data.jsonObj, outsideViewer); return data; }) - .then(contentExtractor.filterJSON(decodeURIComponent(document.location.hash.substring(1)))) + .then(jsonFilter.applyQuery(filterQuery)) .then(contentExtractor.formatJSON) .then(function(data) { return loadRequiredCss(options).then(function() { return data; });