Include Tools

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

当前为 2019-03-18 提交的版本,查看 最新版本

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