UNPKG

table-exporter

Version:

Export HTML Table (Table Tag, Table CSS) to a file (JSON, CSV, etc.)

1,888 lines (1,533 loc) 863 kB
(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