html-utils

utils for DOM manipulation and fetching info of a webpage

当前为 2016-09-06 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/20131/146270/html-utils.js

  1. // ==UserScript==
  2. // @name html-utils
  3. // @name:de html-utils
  4. // @namespace dannysaurus.camamba
  5. // @grant GM_xmlhttpRequest
  6. // @require https://gf.qytechs.cn/scripts/22752-object-utils/code/object-utils.js
  7. // @version 0.1
  8. // @license MIT License
  9. // @description utils for DOM manipulation and fetching info of a webpage
  10. // @description:de utils for DOM manipulation and fetching info of a webpage
  11. // ==/UserScript==
  12.  
  13. var LIB = LIB || {};
  14. /**
  15. * Html Elements
  16. * @type {{Element, Button, Div, Input, Checkbox, Label, TextArea, Option, Select}}
  17. */
  18. LIB.htmlUtils = (function() {
  19. 'use strict';
  20. var objectUtils = LIB.objectUtils;
  21. var keeper = new objectUtils.Keeper();
  22. /**
  23. * Adds a HTML child node to a HTML parent node.
  24. * @param {HtmlElement|Element} parent - The html parent node
  25. * @param {HtmlElement|Element} child - The html child Element to be connected to the parent node
  26. * @param {boolean} [asFirstChild] - <code>true</code> to have the child be added as the first child before any other child
  27. */
  28. var connectParentChild = function(parent, child, asFirstChild) {
  29. var elParent = parent instanceof Element ? parent.domElement : parent;
  30. var elChild = child instanceof Element ? child.domElement : child;
  31. if (asFirstChild && elParent.hasChildNodes()) {
  32. elParent.insertBefore(elChild, elParent.firstChild);
  33. } else {
  34. elParent.appendChild(elChild);
  35. }
  36. };
  37. /**
  38. * Removes a HTML child element from a HTML parent node.
  39. * @param {HTMLElement} parent - The parent node
  40. * @param {HTMLElement} elChild - The child node to be removed of the parent element
  41. */
  42. var disconnectParentChild = function(parent, elChild) {
  43. var elParent = parent instanceof Element ? parent.domElement : parent;
  44. elParent.removeChild(elChild);
  45. };
  46. /**
  47. * Removes all HTML children from a parent node.
  48. * @param {HTMLElement} parent - The HTML parent node
  49. */
  50. var removeAllChildren = function(parent) {
  51. var elParent = parent instanceof Element ? parent.domElement : parent;
  52. while (elParent.hasChildNodes()) {
  53. elParent.removeChild(elParent.firstChild);
  54. }
  55. };
  56.  
  57. /**
  58. * Sorts options of a html 'select' element from a certain property of the option (e.g. text or value).
  59. * @param {HTMLElement|Element} select - The Select Element to be sorted
  60. * @param {string} [property=text] - The Name of the property from wich the option should be compared like <code>value</code> or <code>text</code>.
  61. * @param {boolean} [isOrderDescending] - <code>true</code> to reverse the sort order.
  62. */
  63. var sortSelectBy = function(select, property, isOrderDescending) {
  64. var elSelect = select instanceof Element ? select.domElement : select;
  65. var propertyName = property || 'text';
  66. var sortOptions = [];
  67. // options to array
  68. for (var i = 0; i <= select.length - 1; i++) {
  69. var option = select[i];
  70. sortOptions.push({
  71. value:option.value,
  72. text:option.text,
  73. selected:option.selected
  74. });
  75. }
  76. // sort array
  77. sortOptions.sort(function (a, b) {
  78. if (a[propertyName] < b[propertyName]) { return isOrderDescending ? 1 : -1; }
  79. if (a[propertyName] > b[propertyName]) { return isOrderDescending ? -1 : 1; }
  80. return 0;
  81. });
  82. // array to options
  83. sortOptions.forEach(function (opt, i) {
  84. select[i].text = opt.text;
  85. select[i].value = opt.value;
  86. select[i].selected = opt.selected;
  87. });
  88. };
  89.  
  90. /**
  91. * Wrapper for any type of HTML element.
  92. * @param {string} tagName - The type of element to be created and value of the <code>nodeName</code> attribute
  93. * @param {string} [id] - The value for the <code>id</code> attribute
  94. * @param {string} [className] - The value for the <code>class</code> attribute
  95. * @constructor
  96. */
  97. function Element(tagName, id, className) {
  98. if (!(this instanceof Element)) {
  99. return new Element(tagName, id, className);
  100. }
  101. var domElement = document.createElement(tagName);
  102. Object.defineProperty(this, "domElement", {
  103. get: function() { return domElement },
  104. configurable: true, enumerable: true
  105. });
  106. if (typeof id !== 'undefined') {
  107. this.domElement.id = id;
  108. }
  109. if (typeof className !== 'undefined') {
  110. this.domElement.className = className;
  111. }
  112. }
  113. Element.prototype = {
  114. constructor: Element,
  115. /**
  116. * Adds this HTML element to a parent node HTML element.
  117. * It will be added as the last child of the node.
  118. * @param {HTMLElement|Element} elParent - The parent HTML node element
  119. */
  120. addAsLastChild : function(elParent) {
  121. var parent = elParent instanceof Element ? elParent.domElement : elParent;
  122. connectParentChild(elParent, this.domElement, false);
  123. },
  124. /**
  125. * Adds this HTML element to a parent node HTML element.
  126. * It will be added as the first child of the node.
  127. * @param {HTMLElement|Element} elParent - The parent HTML node element
  128. */
  129. addAsFirstChild : function(elParent) {
  130. var parent = elParent instanceof Element ? elParent.domElement : elParent;
  131. connectParentChild(elParent, this, true);
  132. },
  133. /**
  134. * Adds this HTML element to a parent node HTML element.
  135. * All children elements will be removed and replaced with this node.
  136. * @param {HTMLElement|Element} elParent - The parent HTML node element
  137. */
  138. addAsOnlyChild : function(elParent) {
  139. var parent = elParent instanceof Element ? elParent.domElement : elParent;
  140. removeAllChildren(elParent);
  141. connectParentChild(elParent, this);
  142. },
  143. /**
  144. * Appends Html node elements to this node as their last children.
  145. * @param {...HTMLElement|Element} elements - Html elements to be added as children.
  146. */
  147. appendChildren : function(elements) {
  148. for (var i = 0; i <= arguments.length - 1; i++) {
  149. var child = arguments[i];
  150. this.domElement.appendChild(child instanceof Element ? child.domElement : child);
  151. }
  152. },
  153. /**
  154. * Adds Html node Elements to this node as their new children.
  155. * All current children elements will be removed and replaced with the new children.
  156. * @param {...HTMLElement} [elements] Html elements to be added as children
  157. * No argument will only remove all children.
  158. */
  159. setChildren : function(elements) {
  160. removeAllChildren(this);
  161. for (var i = 0; i <= arguments.length - 1; i++) {
  162. connectParentChild(this, arguments[i]);
  163. }
  164. }
  165. };
  166.  
  167. /**
  168. * Wrapper for a 'button' html element.
  169. * @param {string} [className] - the value for the <code>class</code> attribute
  170. * @param {function} [callback] - the callback function for the <code>onclick<code> event
  171. * @param {string} [text] - the content text of the element (text shown on the button)
  172. * @param {string} [id] - the value for the <code>id</code> attribute
  173. * @constructor
  174. */
  175. function Button(className, callback, text, id) {
  176. if (!(this instanceof Button)) {
  177. return new Button(className, callback, text, id);
  178. }
  179. Element.call(this, 'BUTTON', id, className);
  180. if (typeof callback === 'function') {
  181. this.domElement.addEventListener('click', callback);
  182. }
  183. if (typeof text !== 'undefined') {
  184. this.domElement.appendChild(document.createTextNode(text));
  185. }
  186. }
  187. objectUtils.extend(Button).from(Element);
  188.  
  189. /**
  190. * Wrapper for a 'div' html element.
  191. * @param {string} [id] - The value for the <code>id</code> attribute
  192. * @param {string} [className] - The value for the <code>class</code> attribute
  193. * @constructor
  194. */
  195. function Div(id, className) {
  196. if (!(this instanceof Div)) {
  197. return new Div(id, className);
  198. }
  199. Element.call(this, 'DIV', id, className);
  200. }
  201. objectUtils.extend(Div).from(Element);
  202.  
  203. /**
  204. * Wrapper for an 'input' HTML element as a field for text.
  205. * @param {number|string} [maxLength] - The maximum number of characters
  206. * @param {string} [text] - The value of the <code>text</code> attribute (content initially shown in the field)
  207. * @param {string} [className] - The value for the <code>class</code> attribute
  208. * @param {string} [id] - The value for the <code>id</code> attribute
  209. * @constructor
  210. */
  211. function Input(maxLength, text, className, id) {
  212. if (!(this instanceof Input)) {
  213. return new Input(maxLength, text, className, id);
  214. }
  215. Element.call(this, 'INPUT', id, className);
  216. if (typeof maxLength !== 'undefined') {
  217. this.domElement.maxlength = maxLength;
  218. }
  219. if (typeof text !== 'undefined') {
  220. this.domElement.value = text;
  221. }
  222. }
  223. objectUtils.extend(Input).from(Element);
  224.  
  225. /**
  226. * Creates an 'input' HTML element of type 'checkbox'.
  227. * @param {function} [callback] - The callback function for the <code>onChange<code> event
  228. * @param {string} [className] - The value for the <code>class</code> attribute
  229. * @param {string} [id] - The value for the <code>id</code> attribute
  230. * @constructor
  231. */
  232. function Checkbox (callback, className, id) {
  233. if (!(this instanceof Checkbox)) {
  234. return new Checkbox(callback, className, id);
  235. }
  236. Element.call(this, 'INPUT', id, className);
  237. this.domElement.type = 'checkbox';
  238. if (typeof callback === 'function') {
  239. this.domElement.addEventListener('change', callback);
  240. }
  241. }
  242. objectUtils.extend(Checkbox).from(Element);
  243.  
  244. /**
  245. * Wrapper for a 'label' html element.
  246. * @param {string} htmlFor - The value of the <code>for</code> attribute. The id value of another element to bind the label to that element.
  247. * @param {string} [text] - The content text of the element (text shown on the label)
  248. * @param {string} [className] - The value for the <code>class</code> attribute
  249. * @param {string} [id] - The value for the <code>id</code> attribute
  250. * @constructor
  251. */
  252. function Label(htmlFor, text, className, id) {
  253. if (!(this instanceof Label)) {
  254. return new Label(htmlFor, text, className, id);
  255. }
  256. Element.call(this, 'LABEL', id, className);
  257. if (typeof text !== 'undefined') {
  258. this.domElement.appendChild(document.createTextNode(text));
  259. }
  260. if (typeof htmlFor !== 'undefined') {
  261. this.domElement.htmlFor = htmlFor;
  262. }
  263. }
  264. objectUtils.extend(Label).from(Element);
  265.  
  266. /**
  267. * Wrapper for a 'TextArea' html element.
  268. * @param {number|string} [cols] - The number of columns
  269. * @param {number|string} [maxLength] The maximum number of characters
  270. * @param {string} [text] - The value of the <code>text</code> attribute (content initially shown in the field)
  271. * @param {string} [className] - The value for the <code>class</code> attribute
  272. * @param {string} [id] - The value of the <code>id</code> attribute
  273. * @constructor
  274. */
  275. function TextArea(cols, maxLength, text, className, id) {
  276. if (!(this instanceof TextArea)) {
  277. return new TextArea(cols, maxLength, text, className, id);
  278. }
  279. Element.call(this, 'TEXTAREA', id, className);
  280. if (typeof cols !== 'undefined') {
  281. this.domElement.cols = cols;
  282. }
  283. if (typeof maxLength !== 'undefined') {
  284. this.domElement.maxlength = maxLength;
  285. }
  286. if (typeof text !== 'undefined') {
  287. this.domElement.value = text;
  288. }
  289. }
  290. objectUtils.extend(TextArea).from(Element);
  291.  
  292. /**
  293. * Wrapper for an 'option' html element which can be added to a 'select' html element.
  294. * @param text The value of the <code>text</code> attribute shown in the select dropdown
  295. * @param value THe value of the <code>value</code> attribute
  296. * @constructor
  297. */
  298. function Option(text, value) {
  299. if (!(this instanceof Option)) {
  300. return new Option(text, value);
  301. }
  302. Element.call(this, 'OPTION');
  303. this.domElement.text = text;
  304. this.domElement.value = value;
  305. }
  306. objectUtils.extend(Option).from(Element);
  307.  
  308. /**
  309. * Wrapper for a 'Select' html element.
  310. * @param {string} [id] - The value of the <code>id</code> attribute
  311. * @param {function} [callback] - callback function triggered by the events <code>OnChange</code>, <code>OnKeyUp</code> and <code>OnFocus</code>
  312. * @param {string} [className] - The value for the <code>class</code> attribute
  313. * @constructor
  314. */
  315. function Select(className, callback, id) {
  316. if (!(this instanceof Select)) {
  317. return new Select(className, id);
  318. }
  319. Element.call(this, 'SELECT', id, className);
  320.  
  321. var idx = keeper.push({ onChangeCallback : callback });
  322. Object.defineProperty(this, 'idx', { get: function() { return idx } });
  323. Select.prototype.setOnChangeKeyUpFocus(callback);
  324. }
  325. Select.prototype = {
  326. constructor: Select,
  327. addNewOption: function(text, value) {
  328. var newOption = Option(text, value);
  329. this.domElement.add(newOption.domElement);
  330. return newOption;
  331. },
  332. sortOptionsByText: function(isOrderDesc) {
  333. sortSelectBy(this.domElement, 'text', isOrderDesc);
  334. },
  335. sortOptionsByValue: function(isOrderDesc) {
  336. sortSelectBy(this.domElement, 'value', isOrderDesc);
  337. },
  338. /**
  339. * Sets a callback function triggered from the events <code>OnChange</code>, <code>OnKeyUp</code> and <code>OnFocus</code>.
  340. * Removes any former callback function registered to these events.
  341. * @param {function} callback
  342. */
  343. setOnChangeKeyUpFocus: function(callback) {
  344. var newCallback = (typeof callback === 'function') ? callback : objectUtils.emptyFunction;
  345.  
  346. var formerCallback = keeper.get(this.idx).onChangeCallback;
  347. this.domElement.removeEventListener("focus", formerCallback);
  348. this.domElement.removeEventListener("change", formerCallback);
  349. this.domElement.removeEventListener("keyup", formerCallback);
  350.  
  351. keeper.get(this.idx).onChangeCallback = newCallback;
  352. this.domElement.addEventListener("focus", newCallback);
  353. this.domElement.addEventListener("change", newCallback);
  354. this.domElement.addEventListener("keyup", newCallback);
  355. }
  356. };
  357. objectUtils.extend(Select).from(Element);
  358.  
  359. /**
  360. * Parses a param object into a query string and vice verca.
  361. * @param {Object|string} query - the queryObject object or query string
  362. * @return {string|Object}
  363. */
  364. var parseParams = function(query){
  365. var result;
  366. if (typeof query === 'string') {
  367. result = {};
  368. var decode = function(s) {
  369. return decodeURIComponent(s.replace(/\+/g, " "));
  370. };
  371. var match, search = /([^&=]+)=?([^&]*)/g;
  372. while ((match = search.exec(query)) !== null) {
  373. result[decode(match[1])] = decode(match[2]);
  374. }
  375. return result;
  376. } else {
  377. result = "";
  378. Object.keys(query).forEach(function(key, index) {
  379. if (index >= 1) {
  380. result += "&";
  381. }
  382. result += key + "=" + query[key];
  383. });
  384. return result;
  385. }
  386. };
  387.  
  388. /**
  389. * Deals with parameters of a query.
  390. * @param {string|Object} query - the query search as a string or as parameter object in {key:value} form
  391. * @constructor
  392. */
  393. function Params(query) {
  394. if (!(this instanceof Params)) {
  395. return new Params(query);
  396. }
  397. var _params = {};
  398. Object.defineProperties(this, {
  399. /** The {key:value} parameter object */
  400. queryObject: {
  401. get: function() { return _params; },
  402. enumerable: true, configurable: true
  403. },
  404. /** The queryString without leading '?' */
  405. queryString: {
  406. get: function () {
  407. if (!_params || Object.keys(_params).length === 0) {
  408. return "";
  409. } else {
  410. return parseParams(_params);
  411. }
  412. },
  413. set: function(val) {
  414. if (val) {
  415. if (val.charAt(0) === '?') {
  416. val = val.substring(1);
  417. }
  418. _params = parseParams(val);
  419. } else {
  420. _params = {};
  421. }
  422. },
  423. enumerable: true, configurable: true
  424. }
  425. });
  426. if (typeof query === 'string') {
  427. this.queryString = query;
  428. } else {
  429. _params = query;
  430. }
  431. Object.defineProperty(this, "value", {
  432. get: function () { return this.queryString; }
  433. });
  434. }
  435. objectUtils.extend(Params).from(objectUtils.Truthy);
  436.  
  437. var getPageAsync = function(url, onSuccess, onError) {
  438. if (typeof onSuccess !== 'function') { onSuccess = objectUtils.emptyFunction;}
  439. if (typeof onError !== 'function') { onError = objectUtils.emptyFunction;}
  440.  
  441. return GM_xmlhttpRequest({
  442. method: 'GET',
  443. url: url,
  444. onload: function(resp) {
  445. if (resp.status == 200 || resp.status == 304) {
  446. resp.html = new DOMParser().parseFromString(resp.responseText, 'text/html');
  447. onSuccess(resp);
  448. } else {
  449. onError(resp);
  450. }
  451. },
  452. onerror: onError
  453. });
  454. };
  455.  
  456. return {
  457. Element: Element,
  458. Button: Button,
  459. Div : Div,
  460. Input : Input,
  461. Checkbox : Checkbox,
  462. Label : Label,
  463. TextArea : TextArea,
  464. Option : Option,
  465. Select : Select,
  466. Params : Params,
  467. requestPageAsync : getPageAsync
  468. };
  469. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址