Include Tools

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

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

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