Include Tools

Общие инструменты для всех страничек

当前为 2019-10-27 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/379902/744385/Include%20Tools.js

  1. // ==UserScript==
  2. // @name Include Tools
  3. // @namespace scriptomatika
  4. // @author mouse-karaganda
  5. // @description Общие инструменты для всех страничек
  6. // @version 1.28
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. var paramWindow = (typeof unsafeWindow === 'object') ? unsafeWindow : window;
  11.  
  12. (function(unsafeWindow) {
  13. var console = unsafeWindow.console;
  14. var jQuery = unsafeWindow.jQuery;
  15.  
  16. unsafeWindow.__krokodil = {
  17. /**
  18. * Отрисовывает элемент
  19. */
  20. renderElement: function(config) {
  21. // Определяем параметры по умолчанию
  22. var newRenderType = this.setRenderType(config.renderType);
  23. var newConfig = {
  24. // ~~~ название тега ~~~ //
  25. tagName: config.tagName || 'div',
  26. // ~~~ атрибуты тега ~~~ //
  27. attr: config.attr || {},
  28. // ~~~ идентификатор ~~~ //
  29. id: config.id,
  30. // ~~~ имя класса ~~~ //
  31. cls: config.cls,
  32. // ~~~ встроенные стили тега ~~~ //
  33. style: config.style || {},
  34. // ~~~ встроенные данные ~~~ //
  35. dataset: config.dataset || {},
  36. // ~~~ содержимое элемента ~~~ //
  37. innerHTML: this.join(config.innerHTML || ''),
  38. // ~~~ обработчики событий элемента ~~~ //
  39. listeners: config.listeners || {},
  40. // ~~~ родительский элемент для нового ~~~ //
  41. renderTo: this.getIf(config.renderTo),
  42. // ~~~ способ отрисовки: append (по умолчанию), insertBefore, insertAfter, insertFirst, none ~~~ //
  43. renderType: newRenderType
  44. };
  45. var newElement;
  46. if (newConfig.tagName == 'text') {
  47. // Создаем текстовый узел
  48. newElement = document.createTextNode(newConfig.innerHTML);
  49. } else {
  50. // Создаем элемент
  51. newElement = document.createElement(newConfig.tagName);
  52. // Добавляем атрибуты
  53. this.attr(newElement, newConfig.attr);
  54. // Добавляем идентификатор, если указан
  55. if (newConfig.id) {
  56. this.attr(newElement, { 'id': newConfig.id });
  57. }
  58. //console.log('newElement == %o, config == %o, id == ', newElement, newConfig, newConfig.id);
  59. // Добавляем атрибут класса
  60. if (newConfig.cls) {
  61. this.attr(newElement, { 'class': newConfig.cls });
  62. }
  63. // Наполняем содержимым
  64. newElement.innerHTML = newConfig.innerHTML;
  65. // Задаем стиль
  66. this.css(newElement, newConfig.style);
  67. // Задаем встроенные данные
  68. this.extend(newElement.dataset, newConfig.dataset);
  69. // Навешиваем события
  70. var confListeners = newConfig.listeners;
  71. for (var ev in confListeners) {
  72. if (ev != 'scope') {
  73. //console.log('this.on(newElement == %o, ev == %o, newConfig.listeners[ev] == %o, newConfig.listeners.scope == %o)', newElement, ev, newConfig.listeners[ev], newConfig.listeners.scope);
  74. this.on(newElement, ev, newConfig.listeners[ev], newConfig.listeners.scope);
  75. }
  76. }
  77. //console.log('После: tag == %o, listeners == %o', newConfig.tagName, confListeners);
  78. }
  79. // Отрисовываем элемент
  80. var target, returnRender = true;
  81. while (returnRender) {
  82. switch (newConfig.renderType) {
  83. // Не отрисовывать, только создать
  84. case this.enumRenderType['none']: {
  85. returnRender = false;
  86. break;
  87. };
  88. // Вставить перед указанным
  89. case this.enumRenderType['insertBefore']: {
  90. target = newConfig.renderTo || document.body.firstChild;
  91. // если элемент не задан - вернемся к способу по умолчанию
  92. if (target) {
  93. target.parentNode.insertBefore(newElement, target);
  94. returnRender = false;
  95. } else {
  96. newConfig.renderType = this.enumRenderType['default'];
  97. }
  98. break;
  99. };
  100. // Вставить после указанного
  101. case this.enumRenderType['insertAfter']: {
  102. // если элемент не задан - вернемся к способу по умолчанию
  103. if (newConfig.renderTo && newConfig.renderTo.nextSibling) {
  104. target = newConfig.renderTo.nextSibling;
  105. target.parentNode.insertBefore(newElement, target);
  106. returnRender = false;
  107. } else {
  108. newConfig.renderType = this.enumRenderType['default'];
  109. }
  110. break;
  111. };
  112. // Вставить как первый дочерний
  113. case this.enumRenderType['prepend']: {
  114. // если элемент не задан - вернемся к способу по умолчанию
  115. if (newConfig.renderTo && newConfig.renderTo.firstChild) {
  116. target = newConfig.renderTo.firstChild;
  117. target.parentNode.insertBefore(newElement, target);
  118. returnRender = false;
  119. } else {
  120. newConfig.renderType = this.enumRenderType['default'];
  121. }
  122. break;
  123. };
  124. // Вставить как последний дочерний
  125. case this.enumRenderType['append']:
  126. default: {
  127. var parent = newConfig.renderTo || document.body;
  128. parent.appendChild(newElement);
  129. returnRender = false;
  130. };
  131. }
  132. }
  133. // Возвращаем элемент
  134. return newElement;
  135. },
  136. /**
  137. * Отрисовать несколько одинаковых элементов подряд
  138. */
  139. renderElements: function(count, config) {
  140. for (var k = 0; k < count; k++) {
  141. this.renderElement(config);
  142. }
  143. },
  144. /**
  145. * Отрисовать текстовый узел
  146. */
  147. renderText: function(config) {
  148. // Упрощенные настройки
  149. var newConfig = {
  150. tagName: 'text',
  151. innerHTML: config.text,
  152. renderTo: config.renderTo,
  153. renderType: config.renderType
  154. };
  155. var newElement = this.renderElement(newConfig);
  156. return newElement;
  157. },
  158. /**
  159. * Отрисовать элемент style
  160. * @param {String} text Любое количество строк через запятую
  161. */
  162. renderStyle: function(text) {
  163. var stringSet = arguments;
  164. var tag = this.renderElement({
  165. tagName: 'style',
  166. attr: { type: 'text/css' },
  167. innerHTML: this.format('\n\t{0}\n', this.join(stringSet, '\n\t'))
  168. });
  169. return tag;
  170. },
  171. /**
  172. * Возможные способы отрисовки
  173. */
  174. enumRenderType: {
  175. 'append': 0,
  176. 'prepend': 1,
  177. 'insertBefore': 2,
  178. 'insertAfter': 3,
  179. 'none': 4,
  180. 'default': 0
  181. },
  182. // Назначает способ отрисовки
  183. setRenderType: function(renderType) {
  184. if (typeof renderType != 'string') {
  185. return this.enumRenderType['default'];
  186. }
  187. if (this.enumRenderType[renderType] == undefined) {
  188. return this.enumRenderType['default'];
  189. }
  190. return this.enumRenderType[renderType];
  191. },
  192. /**
  193. * Карта кодов клавиш
  194. */
  195. keyMap: {
  196. // Клавиши со стрелками
  197. arrowLeft: 37,
  198. arrowUp: 38,
  199. arrowRight: 39,
  200. arrowDown: 40
  201. },
  202. /**
  203. * Карта кодов символов
  204. */
  205. charMap: {
  206. arrowLeft: 8592, // ←
  207. arrowRight: 8594 // →
  208. },
  209. /**
  210. * Ждём, пока отрисуется элемент, и выполняем действия
  211. * @param {String} selector css-селектор для поиска элемента (строго строка)
  212. * @param {Function} callback Функция, выполняющая действия над элементом. this внутри неё — искомый DOM-узел
  213. * @param {Number} maxIterCount Максимальное количество попыток найти элемент
  214. */
  215. missingElement: function(selector, callback, maxIterCount) {
  216. var setLog = false;
  217. // Итерации 5 раз в секунду
  218. var missingOne = 100;
  219. // Ограничим количество попыток разумными пределами
  220. var defaultCount = 3000;
  221. if (!this.isNumber(maxIterCount)) {
  222. maxIterCount = defaultCount;
  223. }
  224. if (0 > maxIterCount || maxIterCount > defaultCount) {
  225. maxIterCount = defaultCount;
  226. }
  227. // Запускаем таймер на поиск
  228. var iterCount = 0;
  229. var elementTimer = setInterval(this.createDelegate(function() {
  230. // Сообщение об ожидании
  231. var secondsMsg = this.numberWithCase(iterCount, 'секунду', 'секунды', 'секунд');
  232. if (iterCount % 10 == 0) {
  233. if (setLog) console.log('missing: Ждём [%o] %s', selector, secondsMsg);
  234. }
  235. var element = this.getAll(selector);
  236. // Определим, что вышел элемент
  237. var elementStop = this.isIterable(element) && (element.length > 0);
  238. // Определим, что кончилось количество попыток
  239. var iterStop = (iterCount >= maxIterCount);
  240. if (elementStop || iterStop) {
  241. clearInterval(elementTimer);
  242. var elementExists = true;
  243. // Если элемент так и не появился
  244. if (!elementStop && iterStop) {
  245. if (setLog) console.log('missing: Закончились попытки [%o]', selector);
  246. elementExists = false;
  247. return;
  248. }
  249. // Появился элемент - выполняем действия
  250. if (setLog) console.log('missing: Появился элемент [%o] == %o', selector, element);
  251. if (this.isFunction(callback)) {
  252. if (this.isIterable(element) && (element.length == 1)) {
  253. element = element[0];
  254. }
  255. if (setLog) console.log('missing: Запускаем обработчик [%o] == %o', elementExists, element);
  256. callback.call(element, elementExists);
  257. }
  258. }
  259. iterCount++;
  260. }, this), missingOne);
  261. },
  262. /**
  263. * Добавить свойства в объект
  264. */
  265. extend: function(target, newProperties) {
  266. if (typeof newProperties == 'object') {
  267. for (var i in newProperties) {
  268. target[i] = newProperties[i];
  269. }
  270. }
  271. return target;
  272. },
  273. /**
  274. * Создать класс-наследник от базового класса или объекта
  275. */
  276. inherit: function(base, newConfig) {
  277. var newProto = (typeof base == 'function') ? new base() : this.extend({}, base);
  278. this.extend(newProto, newConfig);
  279. return function() {
  280. var F = function() {};
  281. F.prototype = newProto;
  282. return new F();
  283. };
  284. },
  285. /**
  286. * Получить элемент по селектору
  287. */
  288. get: function(selector, parent) {
  289. parent = this.getIf(parent);
  290. return (parent || unsafeWindow.document).querySelector(selector);
  291. },
  292. /**
  293. * Получить массив элементов по селектору
  294. */
  295. getAll: function(selector, parent) {
  296. parent = this.getIf(parent);
  297. return (parent || unsafeWindow.document).querySelectorAll(selector);
  298. },
  299. /**
  300. * Получить элемент, если задан элемент или селектор
  301. */
  302. getIf: function(element) {
  303. return this.isString(element) ? this.get(element) : element;
  304. },
  305. /**
  306. * Получить массив элементов, если задан массив элементов или селектор
  307. */
  308. getIfAll: function(elements) {
  309. return this.isString(elements) ? this.getAll(elements) : this.toIterable(elements);
  310. },
  311. /**
  312. * Назначим атрибуты элементу или извлечем их
  313. */
  314. attr: function(element, attributes) {
  315. var nativeEl = this.getIf(element);
  316. if (typeof attributes == 'string') {
  317. // извлечем атрибут
  318. var result = '';
  319. if (nativeEl.getAttribute) {
  320. result = nativeEl.getAttribute(attributes);
  321. }
  322. if (!result) {
  323. result = '';
  324. }
  325. return result;
  326. } else if (typeof attributes == 'object') {
  327. // назначим атрибуты всем элементам по селектору
  328. nativeEl = this.getIfAll(element);
  329. for (var i = 0; i < nativeEl.length; i++) {
  330. // назначим атрибуты из списка
  331. for (var at in attributes) {
  332. try {
  333. if (attributes[at] == '') {
  334. // Удалим пустой атрибут
  335. nativeEl[i].removeAttribute(at);
  336. } else {
  337. // Запишем осмысленный атрибут
  338. nativeEl[i].setAttribute(at, attributes[at]);
  339. }
  340. } catch (e) {
  341. console.error(e);
  342. }
  343. }
  344. }
  345. }
  346. },
  347. /**
  348. * Назначим стили элементу или извлечем их
  349. */
  350. css: function(element, properties) {
  351. var nativeEl = this.getIf(element);
  352. if (typeof properties == 'string') {
  353. // извлечем стиль
  354. var result = '';
  355. if (nativeEl.style) {
  356. var calcStyle = window.getComputedStyle(nativeEl, null) || nativeEl.currentStyle;
  357. result = calcStyle[properties];
  358. }
  359. if (!result) {
  360. result = '';
  361. }
  362. return result;
  363. } else if (typeof properties == 'object') {
  364. // присвоим стили всем элементам по селектору
  365. nativeEl = this.getIfAll(element);
  366. try {
  367. for (var i = 0; i < nativeEl.length; i++) {
  368. // назначим стили из списка
  369. this.extend(nativeEl[i].style, properties);
  370. }
  371. } catch (e) {
  372. console.error(e);
  373. }
  374. }
  375. },
  376. /**
  377. * Показать элемент
  378. */
  379. show: function(element, inline) {
  380. var current = this.getIf(element);
  381. if (current) {
  382. var style = current.style;
  383. style.display = inline ? 'inline' : 'block';
  384. }
  385. },
  386. /**
  387. * Спрятать элемент
  388. */
  389. hide: function(element, soft) {
  390. var current = this.getIf(element);
  391. if (current) {
  392. if (!!soft) {
  393. current.style.visibility = 'hidden';
  394. } else {
  395. current.style.display = 'none';
  396. }
  397. }
  398. },
  399. /**
  400. * Спрятать элемент, убрав его за границу экрана
  401. */
  402. hideFixed: function(element) {
  403. var current = this.getIf(element);
  404. if (current) {
  405. this.css(current, {
  406. position: 'fixed',
  407. left: '-2000px',
  408. top: '-2000px'
  409. });
  410. }
  411. },
  412. /**
  413. * Удалить элемент
  414. */
  415. del: function(element) {
  416. var current = this.getIf(element);
  417. if (current && current.parentNode) {
  418. current.parentNode.removeChild(current);
  419. }
  420. },
  421. /**
  422. * Изменить видимость элемента
  423. */
  424. toggle: function(element, inline) {
  425. this.isVisible(element) ? this.hide(element) : this.show(element, inline);
  426. },
  427. /**
  428. * Проверить, виден ли элемент
  429. */
  430. isVisible: function(element) {
  431. return this.getIf(element).style.display != 'none';
  432. },
  433. /**
  434. * Навесить обработчик
  435. */
  436. on: function(element, eventType, handler, scope) {
  437. var elements;
  438. if (!element) {
  439. return false;
  440. }
  441. if (this.isString(element)) {
  442. element = this.getIfAll(element);
  443. if (!(element && this.isIterable(element)))
  444. return false;
  445. }
  446. if (!this.isIterable(element)) {
  447. element = this.toIterable(element);
  448. }
  449. var eventHandler = handler;
  450. if (scope) {
  451. eventHandler = this.createDelegate(handler, scope, handler.arguments);
  452. }
  453. this.each(element, function(currentEl) {
  454. if (currentEl.addEventListener) {
  455. currentEl.addEventListener(eventType, eventHandler, false);
  456. }
  457. else if (currentEl.attachEvent) {
  458. currentEl.attachEvent('on' + eventType, eventHandler);
  459. }
  460. }, this);
  461. },
  462. /**
  463. * Запустить событие
  464. */
  465. fireEvent: function(element, eventType, keys, bubbles, cancelable) {
  466. // Определим необходимые параметры
  467. var eventBubbles = this.isBoolean(bubbles) ? bubbles : true;
  468. var eventCancelable = this.isBoolean(cancelable) ? cancelable : true;
  469. // Для клика создадим MouseEvent
  470. var isMouse = /click|dblclick|mouseup|mousedown/i.test(eventType);
  471. // Приведем к нужному виду клавиши
  472. keys = keys || {};
  473. this.each(['ctrlKey', 'altKey', 'shiftKey', 'metaKey'], function(letter) {
  474. if (!keys[letter]) {
  475. keys[letter] = false;
  476. }
  477. });
  478. // запустим для всех элементов по селектору
  479. var nativeEl = this.getIfAll(element);
  480. this.each(nativeEl, function(elem) {
  481. var evt = document.createEvent(isMouse ? 'MouseEvents' : 'HTMLEvents');
  482. if (isMouse) {
  483. // Событие мыши
  484. // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
  485. evt.initMouseEvent(eventType, eventBubbles, eventCancelable, window, 0, 0, 0, 0, 0, keys.ctrlKey, keys.altKey, keys.shiftKey, keys.metaKey, 0, null);
  486. } else {
  487. // Событие общего типа
  488. // event.initEvent(type, bubbles, cancelable);
  489. evt.initEvent(eventType, eventBubbles, eventCancelable);
  490. }
  491. //var evt = (isMouse ? new MouseEvent() : new UIEvent());
  492. elem.dispatchEvent(evt);
  493. //console.log('dispatchEvent elem == %o, event == %o', elem, evt);
  494. }, this);
  495. },
  496. /**
  497. * Остановить выполнение события
  498. */
  499. stopEvent: function(e) {
  500. var event = e || window.event;
  501. if (!event) {
  502. return false;
  503. }
  504. event.preventDefault = event.preventDefault || function() {
  505. this.returnValue = false;
  506. };
  507. event.stopPropagation = event.stopPropagation || function() {
  508. this.cancelBubble = true;
  509. };
  510. event.preventDefault();
  511. event.stopPropagation();
  512. return true;
  513. },
  514. /**
  515. * Выделить текст в поле ввода
  516. */
  517. selectText: function(element, start, end) {
  518. var current = this.getIf(element);
  519. if (!current) {
  520. return;
  521. }
  522. if (!end) {
  523. end = start;
  524. }
  525. // firefox
  526. if ('selectionStart' in element) {
  527. element.setSelectionRange(start, end);
  528. element.focus(); // to make behaviour consistent with IE
  529. }
  530. // ie win
  531. else if(document.selection) {
  532. var range = element.createTextRange();
  533. range.collapse(true);
  534. range.moveStart('character', start);
  535. range.moveEnd('character', end - start);
  536. range.select();
  537. }
  538. },
  539. /**
  540. * Определяет, является ли значение строкой
  541. */
  542. isString : function(v) {
  543. return typeof v === 'string';
  544. },
  545. /**
  546. * Определяет, является ли значение числом
  547. */
  548. isNumber: function(v) {
  549. return typeof v === 'number' && isFinite(v);
  550. },
  551. /**
  552. * Определяет, является ли значение булевым
  553. */
  554. isBoolean: function(v) {
  555. return typeof v === 'boolean';
  556. },
  557. /**
  558. * Определяет, является ли значение объектом
  559. */
  560. isObject : function(v) {
  561. return typeof v === 'object';
  562. },
  563. /**
  564. * Определяет, является ли значение функцией
  565. */
  566. isFunction: function(v) {
  567. return typeof v === 'function';
  568. },
  569. /**
  570. * Определяет, является ли значение датой
  571. */
  572. isDate: function(v) {
  573. var result = true;
  574. this.each([
  575. 'getDay',
  576. 'getMonth',
  577. 'getFullYear',
  578. 'getHours',
  579. 'getMinutes'
  580. ], function(property) {
  581. result == result && this.isFunction(v[property]);
  582. }, this);
  583. return result;
  584. },
  585. /**
  586. * Переведем число в удобочитаемый вид с пробелами
  587. */
  588. numberToString: function(v) {
  589. var partLen = 3;
  590. try {
  591. v = Number(v);
  592. } catch (e) {
  593. return v;
  594. }
  595. v = String(v);
  596. var pointPos;
  597. pointPos = (pointPos = v.indexOf('.')) > 0 ? (pointPos) : (v.length);
  598. var result = v.substring(pointPos);
  599. v = v.substr(0, pointPos);
  600. var firstPart = true;
  601. while (v.length > 0) {
  602. var startPos = v.length - partLen;
  603. if (startPos < 0) {
  604. startPos = 0;
  605. }
  606. if (!firstPart) {
  607. result = ' ' + result;
  608. }
  609. firstPart = false;
  610. result = v.substr(startPos, partLen) + result;
  611. v = v.substr(0, v.length - partLen);
  612. }
  613. return result;
  614. },
  615. /**
  616. * Число с текстом в нужном падеже
  617. * @param {Number} number Число, к которому нужно дописать текст
  618. * @param {String} textFor1 Текст для количества 1
  619. * @param {String} textFor2 Текст для количества 2
  620. * @param {String} textFor10 Текст для количества 10
  621. */
  622. numberWithCase: function(number, textFor1, textFor2, textFor10) {
  623. // Определяем, какой текст подставить, по последней цифре
  624. var lastDigit = number % 10;
  625. var result = {
  626. number: number,
  627. text: ''
  628. };
  629. // Текст для количества 1
  630. if (this.inArray(lastDigit, [ 1 ])) {
  631. result.text = textFor1;
  632. }
  633. // Текст для количества 2
  634. if (this.inArray(lastDigit, [ 2, 3, 4 ])) {
  635. result.text = textFor2;
  636. }
  637. // Текст для количества 10
  638. if (this.inArray(lastDigit, [ 5, 6, 7, 8, 9, 0 ])) {
  639. result.text = textFor10;
  640. }
  641. // Текст для количества от 11 до 19
  642. var twoLastDigits = number % 100;
  643. if (10 < twoLastDigits && twoLastDigits < 20) {
  644. result.text = textFor10;
  645. }
  646. return this.template('{number} {text}', result);
  647. },
  648. /**
  649. * Определить, является ли тип значения скалярным
  650. */
  651. isScalar: function(v) {
  652. return this.isString(v) || this.isNumber(v) || this.isBoolean(v);
  653. },
  654. /**
  655. * Определить, является ли тип значения перечислимым
  656. */
  657. isIterable: function(v) {
  658. var result = !!v;
  659. if (result) {
  660. result = result && this.isNumber(v.length);
  661. result = result && !this.isString(v);
  662. // У формы есть свойство length - пропускаем её
  663. result = result && !(v.tagName && v.tagName.toUpperCase() == 'FORM');
  664. }
  665. return result;
  666. },
  667. /**
  668. * Сделать значение перечислимым
  669. */
  670. toIterable: function(value) {
  671. if (!value) {
  672. return value;
  673. }
  674. return this.isIterable(value) ? value : [value];
  675. },
  676. /**
  677. * Задать область видимости (scope) для функции
  678. */
  679. createDelegate: function(func, scope, args) {
  680. var method = func;
  681. return function() {
  682. var callArgs = args || arguments;
  683. return method.apply(scope || window, callArgs);
  684. };
  685. },
  686. /**
  687. * Проверим, является ли значение элементом массива или объекта
  688. */
  689. inArray: function(value, array) {
  690. return this.each(array, function(key) {
  691. if (key === value) {
  692. return true;
  693. }
  694. }) !== true;
  695. },
  696. /**
  697. * Найдем значение в массиве и вернем индекс
  698. */
  699. findInArray: function(value, array) {
  700. var result = this.each(array, function(key) {
  701. if (key === value) {
  702. return true;
  703. }
  704. });
  705. return this.isNumber(result) ? result : -1;
  706. },
  707. /**
  708. * Запустить функцию для всех элементов массива или объекта
  709. * @param {Array} array Массив, в котором значения будут перебираться по индексу элемента
  710. * @param {Object} array Объект, в котором значения будут перебираться по имени поля
  711. * @returns {Number} Индекс элемента, на котором досрочно завершилось выполнение, если array - массив
  712. * @returns {String} Имя поля, на котором досрочно завершилось выполнение, если array - объект
  713. * @returns {Boolean} True, если выполнение не завершалось досрочно
  714. */
  715. each: function(array, fn, scope) {
  716. if (!array) {
  717. return;
  718. }
  719. if (this.isIterable(array)) {
  720. for (var i = 0, len = array.length; i < len; i++) {
  721. if (this.isBoolean( fn.call(scope || array[i], array[i], i, array) )) {
  722. return i;
  723. };
  724. }
  725. } else {
  726. for (var key in array) {
  727. if (this.isBoolean( fn.call(scope || array[key], array[key], key, array) )) {
  728. return key;
  729. };
  730. }
  731. }
  732. return true;
  733. },
  734. /**
  735. * Разбить строку, укоротив её и склеив части указанным разделителем
  736. * @param {String} original Исходная строка
  737. * @param {Number} maxLength Максимальная длина, до которой нужно усечь исходную строку
  738. * @param {Number} tailLength Длина второй короткой части
  739. * @param {String} glue Разделитель, который склеит две части укороченной строки
  740. */
  741. splitWithGlue: function(original, maxLength, tailLength, glue) {
  742. // Разделитель по умолчанию
  743. if (!this.isString(glue)) {
  744. glue = '...';
  745. }
  746. // По умолчанию строка завершается разделителем
  747. if (!this.isNumber(tailLength)) {
  748. tailLength = 0;
  749. }
  750. var result = original;
  751. if (result.length > maxLength) {
  752. result = this.template('{head}{glue}{tail}', {
  753. head: original.substring(0, maxLength - (tailLength + glue.length)),
  754. glue: glue,
  755. tail: original.substring(original.length - tailLength)
  756. });
  757. }
  758. return result;
  759. },
  760. /**
  761. * форматирование строки, используя объект
  762. */
  763. template: function(strTarget, objSource) {
  764. var s = arguments[0];
  765. for (var prop in objSource) {
  766. var reg = new RegExp("\\{" + prop + "\\}", "gm");
  767. s = s.replace(reg, objSource[prop]);
  768. }
  769. return s;
  770. },
  771. /**
  772. * форматирование строки, используя числовые индексы
  773. */
  774. format: function() {
  775. var original = arguments[0];
  776. this.each(arguments, function(sample, index) {
  777. if (index > 0) {
  778. var currentI = index - 1;
  779. var reg = new RegExp("\\{" + currentI + "\\}", "gm");
  780. original = original.replace(reg, sample);
  781. }
  782. });
  783. return original;
  784. },
  785. /**
  786. * Быстрый доступ к форматированию
  787. */
  788. fmt: function() {
  789. return this.format.apply(this, arguments);
  790. },
  791. /**
  792. * Выдать строку заданной длины с заполнением символом
  793. */
  794. leftPad: function (val, size, character) {
  795. var result = String(val);
  796. if (!character) {
  797. character = ' ';
  798. }
  799. while (result.length < size) {
  800. result = character + result;
  801. }
  802. return result;
  803. },
  804. /**
  805. * Определить, какая часть окна ушла вверх при прокрутке
  806. */
  807. getScrollOffset: function () {
  808. var d = unsafeWindow.top.document;
  809. return top.pageYOffset ? top.pageYOffset : (
  810. (d.documentElement && d.documentElement.scrollTop) ? (d.documentElement.scrollTop) : (d.body.scrollTop)
  811. );
  812. },
  813. /**
  814. * Определить размер окна
  815. */
  816. getWindowSize: function () {
  817. var d = unsafeWindow.top.document;
  818. return {
  819. width: /*top.innerWidth ? top.innerWidth :*/ (
  820. (d.documentElement.clientWidth) ? (d.documentElement.clientWidth) : (d.body.offsetWidth)
  821. ),
  822. height: /*top.innerHeight ? top.innerHeight :*/ (
  823. (d.documentElement.clientHeight) ? (d.documentElement.clientHeight) : (d.body.offsetHeight)
  824. )
  825. };
  826. },
  827. /**
  828. * Склеить строки
  829. */
  830. join: function(rows, glue) {
  831. return Array.prototype.slice.call(this.toIterable(rows), 0).join(glue || '');
  832. },
  833. /**
  834. * Вернем значение cookie
  835. */
  836. getCookie: function(name) {
  837. var value = null;
  838. // Проверим, есть ли кука с таким именем
  839. var cookie = unsafeWindow.document.cookie;
  840. var regKey = new RegExp(name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + '=(.*?)((; ?)|$)');
  841. var hasMatch = cookie.match(regKey);
  842. if (hasMatch && hasMatch[1]) {
  843. value = decodeURIComponent(hasMatch[1]);
  844. }
  845. return value;
  846. },
  847. /**
  848. * Установим значение cookie
  849. * @param {Object} options Объект с дополнительными значениями
  850. * - expires Срок действия куки: {Number} количество дней или {Data} дата окончания срока
  851. * - path Путь, отсчитывая от которого будет действовать кука
  852. * - domain Домен, в пределах которого будет действовать кука
  853. * - secure Кука для https-соединения
  854. */
  855. setCookie: function(name, value, options) {
  856. // Можно опустить значение куки, если нужно удалить
  857. if (!value) {
  858. value = '';
  859. }
  860. options = options || {};
  861. // Проверяем, задана дата или количество дней
  862. var expires = '';
  863. if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
  864. var date;
  865. if (typeof options.expires == 'number') {
  866. date = new Date();
  867. date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
  868. } else {
  869. date = options.expires;
  870. }
  871. expires = '; expires=' + date.toUTCString();
  872. }
  873. // Проставляем другие опции
  874. var path = options.path ? '; path=' + (options.path) : '';
  875. var domain = options.domain ? '; domain=' + (options.domain) : '';
  876. var secure = (options.secure === true) ? '; secure' : '';
  877. unsafeWindow.document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
  878. },
  879. /**
  880. * Картинка с большим пальцем
  881. */
  882. getThumbHand: function() {
  883. var thumbSource;
  884. thumbSource = ( // рука
  885. 'data:image/png;base64,\
  886. iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA0UlEQVR42r3SPwsBYRzA8buSlMFi\
  887. MymDd+A1WEiSUDarwS6TwSyjgUkkfxZh4J0YpQwKk8L36R56uu5Rd1ee+izXPd/nN/xMw+cx/xXI\
  888. ooYxhm4DSbRRxAQ5N4EUmqjKKZ4YOAXmeCjfj1ICddyxwVVGxL0dep+AGK2gBA5oYPZjuoWYSheY\
  889. Iq+52EUMAWS8BHxNUJbfo9ij5XWCEl4Y6QIrpG2X4uggjIh84KQLnFHB2uH1kGHtglis7x5scVF+\
  890. uom6Ye3ByxYIoo+lGvB8fAfecvkwEbIZfswAAAAASUVORK5CYII=');
  891. thumbSource = ( // сообщение
  892. 'data:image/png;base64,\
  893. iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABL0lEQVQ4y2P4//8/AyWYgWoGRLTv\
  894. EALipUD8E4j/48G7gFgFmwEbVx689f/3n7//8YEtJ++DDLkNxGxwA4AcHiD+8/ffv/8fvv37//LT\
  895. v//PPv77/+T9v/8P3/37f+/Nv/+3X/39f+cVxPDqBcdBhpghGyCXM/UAWPIFUOOzD//+PwZqfvD2\
  896. 3/+7UM3XX/z9f/UZxIDOVWdBBnhjNeApUPOjd1DNr//9v/USovkKUPPFJ7gNgHsB5Pz7QFvvvP77\
  897. /yZQ87Xnf/9fBmq+8ARkwB+wAbWLToAMsMQaiCBDkAHINRce/wUbjBaInLii8Q8syubuuAo36P3n\
  898. H2A+UPwy1mjEhoEK7zx/9xWm8TsQ1xKdEoGKe2duuwLS+AWIC0lKykANSkB8D4hT6JcXBswAAPeL\
  899. DyK+4moLAAAAAElFTkSuQmCC');
  900. return thumbSource;
  901. },
  902. /**
  903. * Отладка
  904. */
  905. thumb: function(text) {
  906. var bgImage = this.format('background: url("{0}") no-repeat;', this.getThumbHand());
  907. console.log('%c ', bgImage, text);
  908. },
  909. /**
  910. * Удалим значение cookie
  911. */
  912. removeCookie: function(name) {
  913. this.setCookie(name, null, { expires: -1 });
  914. },
  915. /**
  916. * Отладка
  917. */
  918. groupDir: function(name, object) {
  919. console.group(name);
  920. console.dir(object);
  921. console.groupEnd();
  922. },
  923. /**
  924. * Отладка: ошибка с пользовательским сообщением
  925. */
  926. errorist: function(error, text, parameters) {
  927. var params = Array.prototype.slice.call(arguments, 1);
  928. params.unshift('#FFEBEB');
  929. this.coloredLog(params);
  930. console.error(error);
  931. },
  932. /**
  933. * Отладка: вывод цветной строки
  934. */
  935. coloredLog: function(color, text) {
  936. var params = Array.prototype.slice.call(arguments, 2);
  937. params.unshift('background-color: ' + color + ';');
  938. params.unshift('%c' + text);
  939. console.log.apply(console, params);
  940. },
  941. /**
  942. * XPath-запрос
  943. */
  944. xpath: function(selector) {
  945. var nodes = document.evaluate(selector, document, null, XPathResult.ANY_TYPE, null);
  946. var thisNode = nodes.iterateNext();
  947. while (thisNode) {
  948. //console.log(thisNode.textContent);
  949. thisNode = nodes.iterateNext();
  950. }
  951. },
  952. /**
  953. * Упаковать для хранилища
  954. */
  955. packToStorage: function(objBox) {
  956. var clone = this.extend({}, objBox);
  957. this.each(clone, function(property, index) {
  958. if (typeof property == 'function') {
  959. clone[index] = property.toString();
  960. }
  961. if (typeof property == 'object') {
  962. clone[index] = this.packToStorage(property);
  963. }
  964. }, this);
  965. return JSON.stringify(clone);
  966. },
  967. /**
  968. * Распаковать из хранилища
  969. */
  970. unpackFromStorage: function(objBox) {
  971. var result = {};
  972. try {
  973. result = JSON.parse(objBox);
  974. } catch (e) {
  975. try {
  976. result = eval('(' + objBox + ')');
  977. } catch (e) {
  978. result = objBox;
  979. }
  980. }
  981. if (typeof result == 'object') {
  982. for (var property in result) {
  983. result[property] = this.unpackFromStorage(result[property]);
  984. }
  985. }
  986. return result;
  987. }
  988. };
  989.  
  990. // Добавляем обратный порядок в jQuery
  991. if (typeof jQuery != 'undefined') {
  992. if (typeof jQuery.fn.reverse != 'function') {
  993. jQuery.fn.reverse = function() {
  994. return jQuery(this.get().reverse());
  995. };
  996. }
  997. if (typeof jQuery.fn.softHide != 'function') {
  998. jQuery.fn.softHide = function() {
  999. return jQuery(this).css({ visibility: 'hidden' });
  1000. };
  1001. }
  1002. }
  1003.  
  1004. unsafeWindow.NodeList.prototype.size = () => this.length;
  1005.  
  1006. // форматирование строки
  1007. unsafeWindow.String.prototype.format = unsafeWindow.__krokodil.format;
  1008.  
  1009. //отладка
  1010. unsafeWindow.console.groupDir = unsafeWindow.__krokodil.groupDir;
  1011. unsafeWindow.console.coloredLog = unsafeWindow.__krokodil.coloredLog;
  1012. unsafeWindow.console.errorist = unsafeWindow.__krokodil.errorist;
  1013.  
  1014. //unsafeWindow.__krokodil.thumb('Include Tools');
  1015. //console.coloredLog('#fffbd6', 'Include Tools');
  1016. //console.errorist('Include Tools');
  1017. console.log('💬 Include Tools');
  1018.  
  1019. })(paramWindow);

QingJ © 2025

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