Include Tools

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

目前为 2019-03-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/379902/684381/Include%20Tools.js

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