table-exporter
Version:
Export HTML Table (Table Tag, Table CSS) to a file (JSON, CSV, etc.)
1,888 lines (1,533 loc) • 863 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function (global){(function (){
/*
* Copyright (c) 2020 TYO Lab
* @author Eric Tang (twitter: @_e_tang).
*/
/**
* @file index.js
*/
var TableExporter = require('./lib/exporter');
function isBrowser() {
try {return this===window;}catch(e){ return false;}
}
var TableExporter = require('./lib/exporter');
function Exporter () {
this.$ = null;
this.in_browser = isBrowser();
this.environment = this.environment || (this.in_browser ? "browser" : "node");
}
/**
* Export the html page
*/
Exporter.prototype.export = function (html, tableSelector, selectors, findProcessor) {
this.$ = this.getQuery(html);
// if (!tableSelector) {
// if (_$('table').length)
// tableSelector = 'table';
// else {
// if (_te.in_browser) {
// if (_te.alert && typeof _te.alert === 'function')
// _te.alert("No table found.");
// return;
// }
// else
// throw ("No table selector found, please specify a proper table selector");
// }
// }
return this.exportNode(this.$, tableSelector, selectors, findProcessor);
}
/**
* sometimes it is easier to export rows rather than a single element
*/
Exporter.prototype.exportRows = function (html, selector, findProcessor) {
findProcessor = findProcessor || this.linkProcessor.bind(this);
var $ = this.getQuery(html);
var exporter = new TableExporter($);
var i = 0;
var rows = exporter.exportRows($(this), selector, findProcessor);
return rows;
}
Exporter.prototype.getQuery = function (selector, parent) {
if (this.in_browser) {
if (typeof $ === 'undefined') {
var se = document.createElement('script');
se.type = 'text/javascript';
se.async = true;
se.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(se, s);
}
this.$ = $;
return $(selector || 'html', parent);
}
else {
if (!this.$) {
var cheerio = require('cheerio');
this.$ = cheerio.load(selector);
return this.$;
}
return this.$(selector, parent);
}
}
/**
* export table with a selector for a particular node
*/
Exporter.prototype.linkProcessor = function ($, nodes, x, y, k) {
var urls = [];
if (nodes.length > 0) {
$(nodes).each(function (i, link) {
var url = $(link).attr('href');
var anchor = $(link).text();
urls.push({url: url, anchor: anchor});
});
}
return urls.length > 0 ? {urls: urls} : null;
};
/**
* Export from parsed node by jQuery or Cheerio
*/
Exporter.prototype.exportNode = function (node, tableSelector, selectors, findProcessor) {
var self = this;
var result = {};
var tables = [];
function processNode($node) {
var exporter = new TableExporter($node);
var i = 0;
var $tables;
if (typeof node === 'object' && !tableSelector)
// tableSelector should be set before calling this method
$tables = node;
else {
// if table selector is not set, we would just table
tableSelector = tableSelector || "table";
$tables = this.in_browser ? self.getQuery(tableSelector, $node) : $node(tableSelector);
}
$tables.each(function(index, table) {
var $table = self.getQuery(table || this);
var table = exporter.export($table, i, selectors, findProcessor);
if (null != table)
tables.push(table);
++i;
});
result.tables = tables;
result.exporter = exporter;
return result;
}
return processNode(node);
}
var exporter = new Exporter();
var _te = _te || {};
_te.exporter = exporter;
if (_te.in_browser) {
// don't declare here, make it flexible
// _te.alert = _te.alert || alert.bind(window);
// window.getQuery = getQuery.bind(window);
window._te = _te;
}
else {
// global.getQuery = getQuery.bind(global);
global._te = _te;
}
module.exports = exporter;
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./lib/exporter":2,"cheerio":7}],2:[function(require,module,exports){
(function (process){(function (){
/**
* Copyright (c) 2018-2020 TYONLINE TECHNOLOGY PTY. LTD. (TYO Lab) All rights reserved.
*
* @author Eric Tang (twitter: @_e_tang)
*
* Some code is from: https://bl.ocks.org/kalebdf/ee7a5e7f44416b2116c0
* Authored by: Adila Faruk
*/
var defaultRowDelim = '\n';
var os = process.platform;
if (os == "darwin") {
}
else if (os == "win32" || os == "win64") {
defaultRowDelim = '\r\n';
}
else if (os == "linux") {
}
function TableExporter ($) {
this.$ = $;
// Grab and format a row from the table
function grabRow(i, row){
var $row = $(row);
//for some reason $cols = $row.find('td') || $row.find('th') won't work...
var $cols = $row.find('td');
if(!$cols.length) $cols = $row.find('th');
return $cols.map(grabCol)
.get().join(tmpColDelim);
}
// Grab and format a column from the table
function grabCol(j,col){
var $col = $(col),
$text = $col.text();
return $text.replace('"', '""'); // escape double quotes
}
//------------------------------------------------------------
// Helper Functions for JavaScript objects
//------------------------------------------------------------
// Grab and format a row from the table
function toColumns(rowIndex, row, tableIndex, cellSelector, targetSelector, selectorProcessor) {
var $row = $(row);
//for some reason $cols = $row.find('td') || $row.find('th') won't work...
var $cols = $row.find(cellSelector);
// if(!$cols.length) $cols = $row.find('th');
var colArray = [];
$cols.each((colIndex, col) => {
var $col = $(col),
$text = $col.text().trim();
var obj;
if (targetSelector && selectorProcessor) {
var $nodes = $(targetSelector, col);
if ($nodes.length > 0) {
obj = selectorProcessor($, $nodes, rowIndex, colIndex, tableIndex);
}
}
if (obj) {
obj.text = $text;
}
else {
obj = $text;
}
colArray.push(obj);
});
return colArray; //[$cols.map(toColObj).get()];
}
// Grab and format a column from the table
function toColumn(j, col){
var $col = $(col),
$text = $col.text().trim();
return $text; // .replace('"', '""'); // escape double quotes not here, because it is text
}
this.table = null;
/**
* Table to JSON
*
*
*
* [
* {
* "headers":['', '', ...],
* "rows":[['', '', ...],
* ...
* ]
* }
* ],
* ...
*
* @param $table
* @param k
* @param selector array for targetSelector, rowSelector, headerSelector, cellSelector
* @param callback
*
*/
this.export = function ($table, tableIndex, selector, callback) {
this.table = {};
var targetSelector = null, rowSelector = null, headerSelector = null, headerRowSelector = null, cellSelector = null;
var selectors;
if (typeof selector === "function") {
callback = selector;
selector = null;
}
if (null != selector) {
if (typeof selector === 'object' && !Array.isArray(selector)) {
targetSelector = selector["target-selector"] || null;
cellSelector = selector["cell-selector"] || null;
headerSelector = selector["header-selector"] || null;
rowSelector = selector["row-selector"] || null;
headerRowSelector = selector["header-row-selector"] || null;
}
else {
if (Array.isArray(selector))
selectors = selector;
else
selectors = [selector];
switch (selectors.length) {
case 5:
targetSelector = selectors[4];
case 4:
cellSelector = selectors[3];
case 3:
rowSelector = selectors[2];
case 2:
headerRowSelector = selectors[1];
case 2:
headerSelector = selectors[0];
case 0:
break;
}
}
}
var findHeaderSelector = null;
if (headerRowSelector === null) {
if (headerSelector === 'th')
findHeaderSelector = (rowSelector || 'tr') + ':has(th)'; // 'tr:has(th)'
else {
headerSelector = headerSelector || 'th';
findHeaderSelector = (rowSelector || 'tr') + ':has(' + headerSelector + ')'; // can't use tr alone as header selector
}
}
else {
headerSelector = headerSelector || 'th';
findHeaderSelector = headerRowSelector + ':has(' + headerSelector + ')';
}
var $headerRow = null;
if (findHeaderSelector) {
$headerRow = $table.find(findHeaderSelector);
if ($headerRow.length) {
var $headers = $headerRow.find(headerSelector);
var headersMap = $headers.map(function(index, header) {
return toColumn(index, header);
});
var headers = headersMap.get();
if (headers.length > 0)
this.table.headers = headers;
}
}
var findRowsSelector = (rowSelector || 'tr');
if (cellSelector)
findRowsSelector += ':has(' + cellSelector + ')';
else if (findRowsSelector === 'tr')
findRowsSelector += ':has(td)';
var $rows = $table.find(findRowsSelector);
if ($rows.length) {
var rows = this.exportRows(tableIndex, $rows, cellSelector, targetSelector, callback);
this.table.rows = rows;
}
return this.table;
}
/**
*
*/
this.exportRows = function (tableIndex, $rows, cellSelector, targetSelector, callback) {
var self = this;
var rows = [];
$rows.each(function(rowIndex, $row) {
var ret = self.exportRow(tableIndex, rowIndex, $row, cellSelector, targetSelector, callback);
rows.push(ret);
});
return rows;
}
/**
*
*/
this.exportRow = function (tableIndex, rowIndex, row, cellSelector, targetSelector, cellProcessor) {
var self = this;
cellSelector = cellSelector || 'td';
var $cols = self.$(row).find(cellSelector);
var cols = [];
$cols.each((colIndex, col) => {
var obj;
if (cellProcessor) {
//var $node = self.$(targetSelector, col);
//if ($node.length > 0) {
obj = cellProcessor(tableIndex, rowIndex, colIndex, self.$(col), cols);
//}
}
var $text = toColumn(colIndex, col);
if (obj) {
obj.text = $text;
}
else {
obj = $text;
}
cols.push(obj);
});
return cols;
}
}
module.exports = TableExporter;
}).call(this)}).call(this,require('_process'))
},{"_process":93}],3:[function(require,module,exports){
/*
* Copyright (c) 2020 TYONLINE TECHNOLOGY PTY. LTD. (TYO Lab) All rights reserved.
* @author Eric Tang (twitter: @_e_tang).
*/
/**
* @file table.js
*/
//------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------
// Format the output so it has the appropriate delimiters
function isWindows() {
return typeof navigator !== 'undefined' && navigator && navigator.platform && navigator.platform.indexOf('Win') > -1
}
const defaultRowDelim = isWindows() ? '\r\n' : '\n';
function formatRows(rows, colDelim, rowDelim) {
colDelim = colDelim || ',';
rowDelim = rowDelim || defaultRowDelim;
return rows.get().join(rowDelim)
.split(rowDelim).join(rowDelim)
.split(colDelim).join(colDelim);
}
function formatHeader(rows, colDelim) {
colDelim = colDelim || ',';
return rows.join(colDelim);
}
function newFormatRows(rows, colDelim, rowDelim) {
colDelim = colDelim || ',';
rowDelim = rowDelim || defaultRowDelim;
return rows.join(rowDelim)
.split(rowDelim).join(rowDelim)
.split(colDelim).join(colDelim);
}
function Table(inColDelim, inRowDelim) {
var
// Temporary delimiter characters unlikely to be typed by keyboard
// This is to avoid accidentally splitting the actual contents
tmpColDelim = inColDelim || String.fromCharCode(11) // vertical tab character
,tmpRowDelim = inRowDelim || String.fromCharCode(0) // null character
// styles
this.classTable = "data-table";
this.classCellDivider = "data-table-cell-divider";
this.classHeaderCell = "data-table-header-cell";
this.classTableHeader = "data-table-header";
this.classCell = 'data-table-cell';
this.classRow = 'data-table-row';
this.styleRow = '';
// Rows
this.data_index = 0;
};
//
Table.prototype.createTableHeader = function(headerRow, groupsSize) {
var headers = [];
groupsSize = groupsSize || 1;
for (var g = 0; g < groupsSize; ++g) {
if (g > 0)
headers.push(this.makeCellDividerDiv());
for (var i = 0; i < headerRow.length; i++) {
headers.push(this.makeHeaderCellDiv(i, headerRow[i]));
}
}
return (
this.makeHeaderDiv(headers));
};
Table.prototype.makeCellDiv = function(index, cellData, styleStr) {
return `<div class="${styleStr}">${cellData}</div>`;
}
Table.prototype.makeRowDiv = function(cols, joined_by) {
return `<div class="${this.classRow}" style="${this.styleRow}">`
+ cols.join(joined_by ||' \n') +
`</div>`;
}
Table.prototype.makeHeaderDiv = function(headers, joined_by) {
return `<div class="${this.classTableHeader}">` +
headers.join(joined_by || " \n") +
`</div>`;
}
Table.prototype.makeCellDividerDiv = function() {
return `<div class="${this.classCellDivider}"></div>`;
}
Table.prototype.makeHeaderCellDiv = function(index, headerCol) {
return (`<div class="${this.classHeaderCell}">${headerCol}</div>`);
}
Table.prototype.makeTableDiv = function(rows, joined_by) {
return (
`<div class="${this.classTable}">`
+
rows.join(joined_by ||' \n')
+
`</div>`
);
}
/**
* If you need to make an empty table with just headers
* you need to pass on (headers, []) //
*/
Table.prototype.makeHtmlTable = function(headers, rows, joined_by) {
if (!rows && headers && headers.length) {
rows = headers;
headers = null;
}
var table_array = [];
if (rows && rows.length) {
var rowsSize = rows.length;
var groupsSize = 1;
if (this.splitToColumnGroupSize > 0 && rowsSize > this.splitToColumnGroupSize)
groupsSize = Math.ceil(rowsSize / this.splitToColumnGroupSize);
// Header
if (headers) {
var header = this.createTableHeader(headers, groupsSize);
table_array.push(header);
}
// Rows
for (var x = this.data_index; x < rowsSize;) {
var cols = [];
// can't remember the purpose of this style
// const styleStr = this.classCell + ((x > 1 && (x % 2) === 0) ? '2' : '');
for (var g = 0; g < groupsSize; g++) {
if (g > 0)
cols.push(this.makeCellDividerDiv());
var dataRow = rows[x];
var colsCount = (dataRow.length && dataRow.length > 0) ? dataRow.length : 0;
for (var i = 0; i < colsCount; i++) {
cols.push(
this.makeCellDiv(i, dataRow[i])
);
}
++x;
if (x >= rowsSize)
break;
}
if (cols.length > 0)
table_array.push(this.makeRowDiv(cols));
cols = [];
}
}
return this.makeTableDiv(table_array);
}
/**
*/
Table.prototype.makeHtmlTables = function (tableObj, headerInRowIndex) {
this.prepareTable();
headerInRowIndex = headerInRowIndex || -1;
var tables = [];
for (var sheetName in tableObj) {
var data = tableObj[sheetName];
// Column Groups
if (!data || !data.length)
continue;
// Header
var headers = null;
if (this.headers || headerInRowIndex > -1) {
headers = this.headers || data[0];
}
var table = this.makeHtmlTable(headers, data);
tables.push(table);
}
return tables;
}
Table.prototype.generate_csv = function (table, cellDelim, rowDelim) {
// Grab text from table into CSV formatted string
var csv = '';
if (table.headers) {
csv += formatHeader(table.headers, cellDelim, rowDelim);
csv += (rowDelim || defaultRowDelim);
}
if (table.rows && table.rows.length)
csv += newFormatRows(table.rows, cellDelim, rowDelim);
return csv;
}
Table.prototype.exportTableToCSV = function($table) {
var $headers = $table.find('tr:has(th)')
,$rows = $table.find('tr:has(td)')
// Grab text from table into CSV formatted string
var csv = '"';
csv += formatRows($headers.map(grabRow));
csv += rowDelim;
csv += formatRows($rows.map(grabRow)) + '"';
return csv;
}
module.exports = Table;
},{}],4:[function(require,module,exports){
/**
* @file table_html_table.js
*
* Making a table with html table tag
*/
const Table = require('./table');
const util = require('util');
function TableHtmlList(inColDelim, inRowDelim) {
Table.call(inColDelim, inRowDelim);
this.classCellDivider = null;
this.classTable = '';
this.classCellDivider = '';
this.classHeaderCell = '';
this.classTableHeader = '';
this.classCell = '';
this.classRow = '';
}
util.inherits(TableHtmlList, Table);
TableHtmlList.prototype.makeCellDiv = function(index, cellData, styleStr) {
return `<div class="${styleStr} col-${index}">${cellData}</div>`;
}
TableHtmlList.prototype.makeRowDiv = function(cols, joined_by) {
return `<li class="${this.classRow}" style="${this.styleRow}">`
+ cols.join(joined_by ||' \n') +
`</li>`;
}
TableHtmlList.prototype.makeHeaderDiv = function(headers, joined_by) {
return `<li class="${this.classTableHeader}">` +
headers.join(joined_by || " \n") +
`</li>`;
}
TableHtmlList.prototype.makeCellDividerDiv = function() {
return `<div class="${this.classCellDivider}"></div>`;
}
TableHtmlList.prototype.makeHeaderCellDiv = function(index, headerCol) {
return (`<div class="${this.classHeaderCell} col-${index}">${headerCol}</div>`);
}
TableHtmlList.prototype.makeTableDiv = function(rows, joined_by) {
return (
`<ul class="${this.classTable}">`
+
rows.join(joined_by ||' \n')
+
`</ul>`
);
}
module.exports = TableHtmlList;
},{"./table":3,"util":98}],5:[function(require,module,exports){
/**
* @file table_html_css.js
*
* Making a table with css
*/
const Table = require('../lib/table');
const util = require('util');
function TableHtmlTable(inColDelim, inRowDelim) {
Table.call(this, inColDelim, inRowDelim);
this.classCellDivider = null;
this.classTable = '';
this.classCellDivider = '';
this.classHeaderCell = '';
this.classTableHeader = '';
this.classCell = '';
this.classRow = '';
}
util.inherits(TableHtmlTable, Table);
TableHtmlTable.prototype.makeCellDiv = function(index, cellData, styleStr) {
return `<td class="${styleStr || ''} col-${index}">${cellData}</td>`;
}
TableHtmlTable.prototype.makeRowDiv = function(cols, joined_by) {
return `<tr class="${this.classRow}" style="${this.styleRow}">`
+ cols.join(joined_by ||' \n') +
`</tr>`;
}
TableHtmlTable.prototype.makeHeaderDiv = function(headers, joined_by) {
return `<tr class="${this.classTableHeader}">` +
headers.join(joined_by || " \n") +
`</tr>`;
}
TableHtmlTable.prototype.makeCellDividerDiv = function() {
if (this.classCellDivider)
return `<div class="${this.classCellDivider}"></div>`;
return '';
}
TableHtmlTable.prototype.makeHeaderCellDiv = function(index, headerCol) {
return (`<th class="${this.classHeaderCell} col-${index}">${headerCol}</th>`);
}
TableHtmlTable.prototype.makeTableDiv = function(rows, joined_by) {
return (
`<table class="${this.classTable}">`
+
rows.join(joined_by ||' \n')
+
`</table>`
);
}
module.exports = TableHtmlTable;
},{"../lib/table":3,"util":98}],6:[function(require,module,exports){
module.exports = {
trueFunc: function trueFunc(){
return true;
},
falseFunc: function falseFunc(){
return false;
}
};
},{}],7:[function(require,module,exports){
/**
* Export cheerio (with )
*/
exports = module.exports = require('./lib/cheerio');
/*
Export the version
*/
exports.version = require('./package.json').version;
},{"./lib/cheerio":13,"./package.json":17}],8:[function(require,module,exports){
var $ = require('../static'),
utils = require('../utils'),
isTag = utils.isTag,
domEach = utils.domEach,
hasOwn = Object.prototype.hasOwnProperty,
camelCase = utils.camelCase,
cssCase = utils.cssCase,
rspace = /\s+/,
dataAttrPrefix = 'data-',
_ = {
forEach: require('lodash.foreach'),
extend: require('lodash.assignin'),
some: require('lodash.some')
},
// Lookup table for coercing string data-* attributes to their corresponding
// JavaScript primitives
primitives = {
null: null,
true: true,
false: false
},
// Attributes that are booleans
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
// Matches strings that look like JSON objects or arrays
rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
var getAttr = function(elem, name) {
if (!elem || !isTag(elem)) return;
if (!elem.attribs) {
elem.attribs = {};
}
// Return the entire attribs object if no attribute specified
if (!name) {
return elem.attribs;
}
if (hasOwn.call(elem.attribs, name)) {
// Get the (decoded) attribute
return rboolean.test(name) ? name : elem.attribs[name];
}
// Mimic the DOM and return text content as value for `option's`
if (elem.name === 'option' && name === 'value') {
return $.text(elem.children);
}
// Mimic DOM with default value for radios/checkboxes
if (elem.name === 'input' &&
(elem.attribs.type === 'radio' || elem.attribs.type === 'checkbox') &&
name === 'value') {
return 'on';
}
};
var setAttr = function(el, name, value) {
if (value === null) {
removeAttribute(el, name);
} else {
el.attribs[name] = value+'';
}
};
exports.attr = function(name, value) {
// Set the value (with attr map support)
if (typeof name === 'object' || value !== undefined) {
if (typeof value === 'function') {
return domEach(this, function(i, el) {
setAttr(el, name, value.call(el, i, el.attribs[name]));
});
}
return domEach(this, function(i, el) {
if (!isTag(el)) return;
if (typeof name === 'object') {
_.forEach(name, function(value, name) {
setAttr(el, name, value);
});
} else {
setAttr(el, name, value);
}
});
}
return getAttr(this[0], name);
};
var getProp = function (el, name) {
if (!el || !isTag(el)) return;
return el.hasOwnProperty(name)
? el[name]
: rboolean.test(name)
? getAttr(el, name) !== undefined
: getAttr(el, name);
};
var setProp = function (el, name, value) {
el[name] = rboolean.test(name) ? !!value : value;
};
exports.prop = function (name, value) {
var i = 0,
property;
if (typeof name === 'string' && value === undefined) {
switch (name) {
case 'style':
property = this.css();
_.forEach(property, function (v, p) {
property[i++] = p;
});
property.length = i;
break;
case 'tagName':
case 'nodeName':
property = this[0].name.toUpperCase();
break;
default:
property = getProp(this[0], name);
}
return property;
}
if (typeof name === 'object' || value !== undefined) {
if (typeof value === 'function') {
return domEach(this, function(i, el) {
setProp(el, name, value.call(el, i, getProp(el, name)));
});
}
return domEach(this, function(i, el) {
if (!isTag(el)) return;
if (typeof name === 'object') {
_.forEach(name, function(val, name) {
setProp(el, name, val);
});
} else {
setProp(el, name, value);
}
});
}
};
var setData = function(el, name, value) {
if (!el.data) {
el.data = {};
}
if (typeof name === 'object') return _.extend(el.data, name);
if (typeof name === 'string' && value !== undefined) {
el.data[name] = value;
} else if (typeof name === 'object') {
_.extend(el.data, name);
}
};
// Read the specified attribute from the equivalent HTML5 `data-*` attribute,
// and (if present) cache the value in the node's internal data store. If no
// attribute name is specified, read *all* HTML5 `data-*` attributes in this
// manner.
var readData = function(el, name) {
var readAll = arguments.length === 1;
var domNames, domName, jsNames, jsName, value, idx, length;
if (readAll) {
domNames = Object.keys(el.attribs).filter(function(attrName) {
return attrName.slice(0, dataAttrPrefix.length) === dataAttrPrefix;
});
jsNames = domNames.map(function(domName) {
return camelCase(domName.slice(dataAttrPrefix.length));
});
} else {
domNames = [dataAttrPrefix + cssCase(name)];
jsNames = [name];
}
for (idx = 0, length = domNames.length; idx < length; ++idx) {
domName = domNames[idx];
jsName = jsNames[idx];
if (hasOwn.call(el.attribs, domName)) {
value = el.attribs[domName];
if (hasOwn.call(primitives, value)) {
value = primitives[value];
} else if (value === String(Number(value))) {
value = Number(value);
} else if (rbrace.test(value)) {
try {
value = JSON.parse(value);
} catch(e){ }
}
el.data[jsName] = value;
}
}
return readAll ? el.data : value;
};
exports.data = function(name, value) {
var elem = this[0];
if (!elem || !isTag(elem)) return;
if (!elem.data) {
elem.data = {};
}
// Return the entire data object if no data specified
if (!name) {
return readData(elem);
}
// Set the value (with attr map support)
if (typeof name === 'object' || value !== undefined) {
domEach(this, function(i, el) {
setData(el, name, value);
});
return this;
} else if (hasOwn.call(elem.data, name)) {
return elem.data[name];
}
return readData(elem, name);
};
/**
* Get the value of an element
*/
exports.val = function(value) {
var querying = arguments.length === 0,
element = this[0];
if(!element) return;
switch (element.name) {
case 'textarea':
return this.text(value);
case 'input':
switch (this.attr('type')) {
case 'radio':
if (querying) {
return this.attr('value');
} else {
this.attr('value', value);
return this;
}
break;
default:
return this.attr('value', value);
}
return;
case 'select':
var option = this.find('option:selected'),
returnValue;
if (option === undefined) return undefined;
if (!querying) {
if (!this.attr().hasOwnProperty('multiple') && typeof value == 'object') {
return this;
}
if (typeof value != 'object') {
value = [value];
}
this.find('option').removeAttr('selected');
for (var i = 0; i < value.length; i++) {
this.find('option[value="' + value[i] + '"]').attr('selected', '');
}
return this;
}
returnValue = option.attr('value');
if (this.attr().hasOwnProperty('multiple')) {
returnValue = [];
domEach(option, function(i, el) {
returnValue.push(getAttr(el, 'value'));
});
}
return returnValue;
case 'option':
if (!querying) {
this.attr('value', value);
return this;
}
return this.attr('value');
}
};
/**
* Remove an attribute
*/
var removeAttribute = function(elem, name) {
if (!elem.attribs || !hasOwn.call(elem.attribs, name))
return;
delete elem.attribs[name];
};
exports.removeAttr = function(name) {
domEach(this, function(i, elem) {
removeAttribute(elem, name);
});
return this;
};
exports.hasClass = function(className) {
return _.some(this, function(elem) {
var attrs = elem.attribs,
clazz = attrs && attrs['class'],
idx = -1,
end;
if (clazz) {
while ((idx = clazz.indexOf(className, idx+1)) > -1) {
end = idx + className.length;
if ((idx === 0 || rspace.test(clazz[idx-1]))
&& (end === clazz.length || rspace.test(clazz[end]))) {
return true;
}
}
}
});
};
exports.addClass = function(value) {
// Support functions
if (typeof value === 'function') {
return domEach(this, function(i, el) {
var className = el.attribs['class'] || '';
exports.addClass.call([el], value.call(el, i, className));
});
}
// Return if no value or not a string or function
if (!value || typeof value !== 'string') return this;
var classNames = value.split(rspace),
numElements = this.length;
for (var i = 0; i < numElements; i++) {
// If selected element isn't a tag, move on
if (!isTag(this[i])) continue;
// If we don't already have classes
var className = getAttr(this[i], 'class'),
numClasses,
setClass;
if (!className) {
setAttr(this[i], 'class', classNames.join(' ').trim());
} else {
setClass = ' ' + className + ' ';
numClasses = classNames.length;
// Check if class already exists
for (var j = 0; j < numClasses; j++) {
var appendClass = classNames[j] + ' ';
if (setClass.indexOf(' ' + appendClass) < 0)
setClass += appendClass;
}
setAttr(this[i], 'class', setClass.trim());
}
}
return this;
};
var splitClass = function(className) {
return className ? className.trim().split(rspace) : [];
};
exports.removeClass = function(value) {
var classes,
numClasses,
removeAll;
// Handle if value is a function
if (typeof value === 'function') {
return domEach(this, function(i, el) {
exports.removeClass.call(
[el], value.call(el, i, el.attribs['class'] || '')
);
});
}
classes = splitClass(value);
numClasses = classes.length;
removeAll = arguments.length === 0;
return domEach(this, function(i, el) {
if (!isTag(el)) return;
if (removeAll) {
// Short circuit the remove all case as this is the nice one
el.attribs.class = '';
} else {
var elClasses = splitClass(el.attribs.class),
index,
changed;
for (var j = 0; j < numClasses; j++) {
index = elClasses.indexOf(classes[j]);
if (index >= 0) {
elClasses.splice(index, 1);
changed = true;
// We have to do another pass to ensure that there are not duplicate
// classes listed
j--;
}
}
if (changed) {
el.attribs.class = elClasses.join(' ');
}
}
});
};
exports.toggleClass = function(value, stateVal) {
// Support functions
if (typeof value === 'function') {
return domEach(this, function(i, el) {
exports.toggleClass.call(
[el],
value.call(el, i, el.attribs['class'] || '', stateVal),
stateVal
);
});
}
// Return if no value or not a string or function
if (!value || typeof value !== 'string') return this;
var classNames = value.split(rspace),
numClasses = classNames.length,
state = typeof stateVal === 'boolean' ? stateVal ? 1 : -1 : 0,
numElements = this.length,
elementClasses,
index;
for (var i = 0; i < numElements; i++) {
// If selected element isn't a tag, move on
if (!isTag(this[i])) continue;
elementClasses = splitClass(this[i].attribs.class);
// Check if class already exists
for (var j = 0; j < numClasses; j++) {
// Check if the class name is currently defined
index = elementClasses.indexOf(classNames[j]);
// Add if stateValue === true or we are toggling and there is no value
if (state >= 0 && index < 0) {
elementClasses.push(classNames[j]);
} else if (state <= 0 && index >= 0) {
// Otherwise remove but only if the item exists
elementClasses.splice(index, 1);
}
}
this[i].attribs.class = elementClasses.join(' ');
}
return this;
};
exports.is = function (selector) {
if (selector) {
return this.filter(selector).length > 0;
}
return false;
};
},{"../static":15,"../utils":16,"lodash.assignin":55,"lodash.foreach":60,"lodash.some":66}],9:[function(require,module,exports){
var domEach = require('../utils').domEach,
_ = {
pick: require('lodash.pick'),
};
var toString = Object.prototype.toString;
/**
* Set / Get css.
*
* @param {String|Object} prop
* @param {String} val
* @return {self}
* @api public
*/
exports.css = function(prop, val) {
if (arguments.length === 2 ||
// When `prop` is a "plain" object
(toString.call(prop) === '[object Object]')) {
return domEach(this, function(idx, el) {
setCss(el, prop, val, idx);
});
} else {
return getCss(this[0], prop);
}
};
/**
* Set styles of all elements.
*
* @param {String|Object} prop
* @param {String} val
* @param {Number} idx - optional index within the selection
* @return {self}
* @api private
*/
function setCss(el, prop, val, idx) {
if ('string' == typeof prop) {
var styles = getCss(el);
if (typeof val === 'function') {
val = val.call(el, idx, styles[prop]);
}
if (val === '') {
delete styles[prop];
} else if (val != null) {
styles[prop] = val;
}
el.attribs.style = stringify(styles);
} else if ('object' == typeof prop) {
Object.keys(prop).forEach(function(k){
setCss(el, k, prop[k]);
});
}
}
/**
* Get parsed styles of the first element.
*
* @param {String} prop
* @return {Object}
* @api private
*/
function getCss(el, prop) {
var styles = parse(el.attribs.style);
if (typeof prop === 'string') {
return styles[prop];
} else if (Array.isArray(prop)) {
return _.pick(styles, prop);
} else {
return styles;
}
}
/**
* Stringify `obj` to styles.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function stringify(obj) {
return Object.keys(obj || {})
.reduce(function(str, prop){
return str += ''
+ (str ? ' ' : '')
+ prop
+ ': '
+ obj[prop]
+ ';';
}, '');
}
/**
* Parse `styles`.
*
* @param {String} styles
* @return {Object}
* @api private
*/
function parse(styles) {
styles = (styles || '').trim();
if (!styles) return {};
return styles
.split(';')
.reduce(function(obj, str){
var n = str.indexOf(':');
// skip if there is no :, or if it is the first/last character
if (n < 1 || n === str.length-1) return obj;
obj[str.slice(0,n).trim()] = str.slice(n+1).trim();
return obj;
}, {});
}
},{"../utils":16,"lodash.pick":63}],10:[function(require,module,exports){
// https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
// https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
var submittableSelector = 'input,select,textarea,keygen',
r20 = /%20/g,
rCRLF = /\r?\n/g,
_ = {
map: require('lodash.map')
};
exports.serialize = function() {
// Convert form elements into name/value objects
var arr = this.serializeArray();
// Serialize each element into a key/value string
var retArr = _.map(arr, function(data) {
return encodeURIComponent(data.name) + '=' + encodeURIComponent(data.value);
});
// Return the resulting serialization
return retArr.join('&').replace(r20, '+');
};
exports.serializeArray = function() {
// Resolve all form elements from either forms or collections of form elements
var Cheerio = this.constructor;
return this.map(function() {
var elem = this;
var $elem = Cheerio(elem);
if (elem.name === 'form') {
return $elem.find(submittableSelector).toArray();
} else {
return $elem.filter(submittableSelector).toArray();
}
}).filter(
// Verify elements have a name (`attr.name`) and are not disabled (`:disabled`)
'[name!=""]:not(:disabled)'
// and cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)
+ ':not(:submit, :button, :image, :reset, :file)'
// and are either checked/don't have a checkable state
+ ':matches([checked], :not(:checkbox, :radio))'
// Convert each of the elements to its value(s)
).map(function(i, elem) {
var $elem = Cheerio(elem);
var name = $elem.attr('name');
var val = $elem.val();
// If there is no value set (e.g. `undefined`, `null`), then return nothing
if (val == null) {
return null;
} else {
// If we have an array of values (e.g. `<select multiple>`), return an array of key/value pairs
if (Array.isArray(val)) {
return _.map(val, function(val) {
// We trim replace any line endings (e.g. `\r` or `\r\n` with `\r\n`) to guarantee consistency across platforms
// These can occur inside of `<textarea>'s`
return {name: name, value: val.replace( rCRLF, '\r\n' )};
});
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
} else {
return {name: name, value: val.replace( rCRLF, '\r\n' )};
}
}
// Convert our result to an array
}).get();
};
},{"lodash.map":61}],11:[function(require,module,exports){
var parse = require('../parse'),
$ = require('../static'),
updateDOM = parse.update,
evaluate = parse.evaluate,
utils = require('../utils'),
domEach = utils.domEach,
cloneDom = utils.cloneDom,
isHtml = utils.isHtml,
slice = Array.prototype.slice,
_ = {
flatten: require('lodash.flatten'),
bind: require('lodash.bind'),
forEach: require('lodash.foreach')
};
// Create an array of nodes, recursing into arrays and parsing strings if
// necessary
exports._makeDomArray = function makeDomArray(elem, clone) {
if (elem == null) {
return [];
} else if (elem.cheerio) {
return clone ? cloneDom(elem.get(), elem.options) : elem.get();
} else if (Array.isArray(elem)) {
return _.flatten(elem.map(function(el) {
return this._makeDomArray(el, clone);
}, this));
} else if (typeof elem === 'string') {
return evaluate(elem, this.options);
} else {
return clone ? cloneDom([elem]) : [elem];
}
};
var _insert = function(concatenator) {
return function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
return domEach(this, function(i, el) {
var dom, domSrc;
if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
} else {
domSrc = elems;
}
dom = this._makeDomArray(domSrc, i < lastIdx);
concatenator(dom, el.children, el);
});
};
};
/*
* Modify an array in-place, removing some number of elements and adding new
* elements directly following them.
*
* @param {Array} array Target array to splice.
* @param {Number} spliceIdx Index at which to begin changing the array.
* @param {Number} spliceCount Number of elements to remove from the array.
* @param {Array} newElems Elements to insert into the array.
*
* @api private
*/
var uniqueSplice = function(array, spliceIdx, spliceCount, newElems, parent) {
var spliceArgs = [spliceIdx, spliceCount].concat(newElems),
prev = array[spliceIdx - 1] || null,
next = array[spliceIdx] || null;
var idx, len, prevIdx, node, oldParent;
// Before splicing in new elements, ensure they do not already appear in the
// current array.
for (idx = 0, len = newElems.length; idx < len; ++idx) {
node = newElems[idx];
oldParent = node.parent || node.root;
prevIdx = oldParent && oldParent.children.indexOf(newElems[idx]);
if (oldParent && prevIdx > -1) {
oldParent.children.splice(prevIdx, 1);
if (parent === oldParent && spliceIdx > prevIdx) {
spliceArgs[0]--;
}
}
node.root = null;
node.parent = parent;
if (node.prev) {
node.prev.next = node.next || null;
}
if (node.next) {
node.next.prev = node.prev || null;
}
node.prev = newElems[idx - 1] || prev;
node.next = newElems[idx + 1] || next;
}
if (prev) {
prev.next = newElems[0];
}
if (next) {
next.prev = newElems[newElems.length - 1];
}
return array.splice.apply(array, spliceArgs);
};
exports.appendTo = function(target) {
if (!target.cheerio) {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
}
target.append(this);
return this;
};
exports.prependTo = function(target) {
if (!target.cheerio) {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
}
target.prepend(this);
return this;
};
exports.append = _insert(function(dom, children, parent) {
uniqueSplice(children, children.length, 0, dom, parent);
});
exports.prepend = _insert(function(dom, children, parent) {
uniqueSplice(children, 0, 0, dom, parent);
});
exports.wrap = function(wrapper) {
var wrapperFn = typeof wrapper === 'function' && wrapper,
lastIdx = this.length - 1;
_.forEach(this, _.bind(function(el, i) {
var parent = el.parent || el.root,
siblings = parent.children,
dom, index;
if (!parent) {
return;
}
if (wrapperFn) {
wrapper = wrapperFn.call(el, i);
}
if (typeof wrapper === 'string' && !isHtml(wrapper)) {
wrapper = this.parents().last().find(wrapper).clone();
}
dom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1);
index = siblings.indexOf(el);
updateDOM([el], dom[0]);
// The previous operation removed the current element from the `siblings`
// array, so the `dom` array can be inserted without removing any
// additional elements.
uniqueSplice(siblings, index, 0, dom, parent);
}, this));
return this;
};
exports.after = function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
domEach(this, function(i, el) {
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el),
domSrc, dom;
// If not found, move on
if (index < 0) return;
if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
} else {
domSrc = elems;
}
dom = this._makeDomArray(domSrc, i < lastIdx);
// Add element after `this` element
uniqueSplice(siblings, index + 1, 0, dom, parent);
});
return this;
};
exports.insertAfter = function(target) {
var clones = [],
self = this;
if (typeof target === 'string') {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
}
target = this._makeDomArray(target);
self.remove();
domEach(target, function(i, el) {
var clonedSelf = self._makeDomArray(self.clone());
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el);
// If not found, move on
if (index < 0) return;
// Add cloned `this` element(s) after target element
uniqueSplice(siblings, index + 1, 0, clonedSelf, parent);
clones.push(clonedSelf);
});
return this.constructor.call(this.constructor, this._makeDomArray(clones));
};
exports.before = function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
domEach(this, function(i, el) {
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el),
domSrc, dom;
// If not found, move on
if (index < 0) return;
if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
} else {
domSrc = elems;
}
dom = this._makeDomArray(domSrc, i < lastIdx);
// Add element before `el` element
uniqueSplice(siblings, index, 0, dom, parent);
});
return this;
};
exports.insertBefore = function(target) {
var clones = [],
self = this;
if (typeof target === 'string') {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
}
target = this._makeDomArray(target);
self.remove();
domEach(target, function(i, el) {
var clonedSelf = self._makeDomArray(self.clone());
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el);
// If not found, move on
if (index < 0) return;
// Add cloned `this` element(s) after target element
uniqueSplice(siblings, index, 0, clonedSelf, parent);
clones.push(clonedSelf);
});
return this.constructor.call(this.constructor, this._makeDomArray(clones));
};
/*
remove([selector])
*/
exports.remove = function(selector) {
var elems = this;
// Filter if we have selector
if (selector)
elems = elems.filter(selector);
domEach(elems, function(i, el) {
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el);
if (index < 0) return;
siblings.splice(index, 1);
if (el.prev) {
el.prev.next = el.next;
}
if (el.next) {
el.next.prev = el.prev;
}
el.prev = el.next = el.parent = el.root = null;
});
return this;
};
exports.replaceWith = function(content) {
var self = this;
domEach(this, function(i, el) {
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
dom = self._makeDomArray(typeof content === 'function' ? content.call(el, i, el) : content),
index;
// In the case that `dom` contains nodes that already exist in other
// structures, ensure those nodes are properly removed.
updateDOM(dom, null);
index = siblings.indexOf(el);
// Completely remove old element
uniqueSplice(siblings, index, 1, dom, parent);
el.parent = el.prev = el.next = el.root = null;
});
return this;
};
exports.empty = function() {
domEach(this, function(i, el) {
_.forEach(el.children, function(el) {
el.next = el.prev = el.parent = null;
});
el.children.length = 0;
});
return this;
};
/**
* Set/Get the HTML
*/
exports.html = function(str) {
if (str === undefined) {
if (!this[0] || !this[0].children) return null;
return $.html(this[0].children, this.options);
}
var opts = this.options;
domEach(this, function(i, el) {
_.forEach(el.children, function(el) {
el.next = el.prev = el.parent = null;
});
var content = str.cheerio ? str.clone().get() : evaluate('' + str, opts);
updateDOM(content, el);
});
return this;
};
exports.toString = function() {
return $.html(this, this.options);
};
exports.text = f