UNPKG

truncate-element

Version:

With this package, you can make any changes you want on the text. Don't worry about the framework. It is a custom element and is compatible with all JavaScript frameworks.

747 lines (732 loc) 29.4 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("truncate-element", [], factory); else if(typeof exports === 'object') exports["truncate-element"] = factory(); else root["truncate-element"] = factory(); })(window, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1); /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); // CONCATENATED MODULE: ./src/interfaces.ts // CONCATENATED MODULE: ./src/services.ts class ProccessModel { constructor(_config) { } } class WordModel { constructor(word) { this.word = ""; this.length = 0; this.type = "simple"; this.html = ""; this.word = word; this.length = word.length; } } class WordCut { /** * create an instance of WordCut with desired number * @param num number */ constructor(num) { this.Number = num; } /** * truncate the string to the desired number * @param text string * @returns string */ cut(text) { let i = 0; for (i = this.Number; i < text.length; i++) { if (text[i] == ' ') break; } return text.substring(0, i); } } class Link extends ProccessModel { constructor(_config) { super(_config); this.LinkCount = 0; this.LinkList = []; if (_config.identifyLink) this.IdentifyLink = _config.identifyLink; else this.IdentifyLink = { enabled: false }; } ; /** * process model for identifying links * @param model WordModel class * @returns WordModel */ process(model) { // main regex // regex = /(^|[ ])(ftp:\/\/|(https?:)\/\/(www\\.)?|www\.)[0-9a-zA-Z]([-.\w]+)(((\:|\/|\?){1}\S*)*|\w+)+/gm let regex; let protocolRegex = "(ftp:\\/\\/|(https?:)\\/\\/(www\\.)?|www\\.)"; let domainRegex = ""; let queryString = "\\w+"; const afterDomain = "(((\\:|\\/|\\?){1}\\S*)*|\\w+)+"; if (this.IdentifyLink.protocol && this.IdentifyLink.protocol.length > 0) protocolRegex = this.recognizingProtocol(); if (this.IdentifyLink.domain && this.IdentifyLink.domain.length > 0) domainRegex = this.recongnizingDomain(); if (this.IdentifyLink.hasQueryString) queryString = afterDomain; regex = new RegExp(`(^|[ ])${protocolRegex}[0-9a-zA-Z]([-.\\w]+)${domainRegex}${queryString}`, 'gm'); let m; while ((m = regex.exec(model.word)) !== null) { this.LinkList.push(m[0]); let protocol = m[0]; if (m[0].substring(0, 3) === 'www') { protocol = 'http://' + m[0]; } model.html = `<a href=${protocol} ${this.IdentifyLink.target ? "target=" + this.IdentifyLink.target : ''} ${this.IdentifyLink.title ? "title=" + this.IdentifyLink.title : ''} ${this.IdentifyLink.class ? "class=" + '"' + this.IdentifyLink.class + '"' : ''} ${this.IdentifyLink.style ? "style=" + this.IdentifyLink.style : ''}>${model.html ? model.html : model.word}</a>`; model.type += ' link'; } return model; } /** create regex for prefix | protocol * @returns {string} regex */ recognizingProtocol() { if (this.IdentifyLink.protocol && this.IdentifyLink.protocol.length > 0) { this.IdentifyLink.protocol = this.IdentifyLink.protocol.sort(); let protocol = []; if (this.IdentifyLink.protocol.find(p => p === 'ftp')) { protocol.push("ftp:\\/\\/"); } if (this.IdentifyLink.protocol.find(p => p === 'http')) { protocol.push(`http:\\/\\/(www\\.)?`); } if (this.IdentifyLink.protocol.find(p => p === 'https')) { protocol.push(`https:\\/\\/(www\\.)?`); } if (this.IdentifyLink.protocol.find(p => p === 'www')) { protocol.push("www\\.?"); } return "(" + protocol.join('|') + ")"; } return ""; } /** create regex for domain * @returns {string} regex */ recongnizingDomain() { return this.IdentifyLink.domain ? "(" + this.IdentifyLink.domain.join('|') + ")" : ""; } } class Mention extends ProccessModel { /** * process model for identifying mentions * @param model {class} WordModel * @returns WordModel */ process(model) { const pattern = /(^|\s)(@[^\d\.\s](\w{1,30}|\.)+)$/gmi; if (pattern.test(model.word)) { model.type += ' mention'; model.html = `<span class=mention style=color:#0095f6>${model.html ? model.html : model.word}</span>`; } return model; } } class Hashtag extends ProccessModel { /** * process model for identifying hashtags * @param model {class} WordModel * @returns WordModel */ process(model) { const pattern = /(^|\s)(#[\p{Pc}\p{N}\p{L}\u200cÀ-ÖØ-öø-ʸ(_)]+)$/gmui; if (pattern.test(model.word)) { model.html = `<span class=hashtag style=color:#1b95e0>${model.html ? model.html : model.word}</span>`; model.type += ' hashtag'; } return model; } } class Highlight extends ProccessModel { constructor(_config) { super(_config); this.highlightQuery = []; this.highlightCondition = ''; this.nestedHighlightArray = []; this.checkedNestedHighlight = false; window.highlight = this; if (_config.highlightList) { this.highlightQuery = _config.highlightList.map((h) => { let high = {}; if (this.instanceOfHighlighQuery(h)) { high.name = h.name; high.color = h.color; } else { high.name = h; high.color = 'yellow'; } high.tag = ''; high.index = 0; high.existInAnotherQuery = false; high.parent = ''; high.parentTag = ''; high.content = ''; return high; }); } else { this.highlightQuery = []; } this.highlightCondition = _config.highlightCondition; if (this.highlightQuery.length) { this.highlightQuery.sort((a, b) => { return a.name.length - b.name.length; }); } if (!this.checkedNestedHighlight) this.nestedHighlightArray = this.findHighlightInAnother(); else this.nestedHighlightArray = []; } /** * finds words in highlightQuery array and color them * @returns Array<Sible> */ findHighlightInAnother() { const firstElement = this.highlightQuery[0]; firstElement.tag = this.createTag(firstElement.color, firstElement.name); firstElement.existInAnotherQuery = false; firstElement.content = ''; firstElement.parent = ''; firstElement.parentTag = ''; for (let i = 1; i < this.highlightQuery.length; i++) { const currentElement = this.highlightQuery[i]; const perviousElement = this.highlightQuery[i - 1]; if (currentElement.name.includes(perviousElement.name)) { this.transform(currentElement, perviousElement); continue; } else { let j = i; while (j >= 1) { let pervious = this.highlightQuery[j - 1]; if (currentElement.name.includes(pervious.name)) { this.transform(currentElement, pervious); break; } else if (j == 1) { currentElement.tag = this.createTag(currentElement.color, currentElement.name); currentElement.content = ''; currentElement.parent = ''; currentElement.parentTag = ''; } j--; } } } const nestedArray = this.highlightQuery.filter(m => m.existInAnotherQuery); this.highlightQuery = this.highlightQuery.filter(m => !m.existInAnotherQuery); //console.log('highlightQuery=>', this.highlightQuery, 'nestedArray=>', nestedArray); this.checkedNestedHighlight = true; return nestedArray; } transform(current, pervious) { // name of current object is parent for pervious object pervious.parent = current.name; current.content = current.name.replace(pervious.name, pervious.tag); pervious.parentTag = this.createTag(current.color, current.content); current.tag = pervious.parentTag; pervious.existInAnotherQuery = true; current.existInAnotherQuery = false; } /** is object instance of HighlighQuery * @param {object} object to check * @returns {boolean} true or false */ instanceOfHighlighQuery(object) { return typeof object === 'string' ? false : 'name' in object; } /** * process model for highlighting * @param model WordModel class * @returns WordModel */ process(model) { if (!this.highlightQuery || !this.highlightQuery.length) { return model; } if (this.highlightCondition == "exactly") { for (let q of this.highlightQuery) { if (q.name.length > model.length) continue; const pattern = "(^|(?<!\\p{L}))(" + q.name + ")(?!(\\p{L}))"; const regex = new RegExp(pattern, 'gmu'); if (regex.test(model.word)) { regex.lastIndex = 0; model.html = this.createTag(q.color, q.name); model.type = model.type.concat(' highlight'); } return model; } return model; } else { if (this.nestedHighlightArray.length) { for (let i = this.nestedHighlightArray.length; i--; i == 0) { const nested = this.nestedHighlightArray[i]; const regex = new RegExp(nested.name, 'gmi'); if (regex.test(model.word)) { regex.lastIndex = 0; model.html = model.word.replace(new RegExp(nested.name, 'gm'), nested.tag); // return model; break; } } } if (this.highlightQuery.length) { for (const q of this.highlightQuery) { const regex = new RegExp(q.name, 'gmi'); if (regex.test(model.word)) { regex.lastIndex = 0; if (model.html && model.html.split(q.name).length == 1) { model.html = model.word.replace(new RegExp(q.name, 'gmu'), q.tag); } else if (model.html) { let tag = model.html.split(q.name); for (let index = 0; index < tag.length; index++) { if (index == tag.length - 1) break; tag[index] = tag[index] + q.tag; } model.html = tag.join(''); } else { model.html = model.word.replace(new RegExp(q.name, 'gmu'), match => { return `<span style="background:${q.color}">${match}</span>`; }); } } } } return model; } } /** * create span tag with favorite color * @param color * @param word * @returns string */ createTag(color, word) { return `<span style="background:${color}">${word}</span>`; } } class NullModel extends ProccessModel { /** * get string and convert to WordModel * @param model WordModel class * @returns WordModel */ process(word) { return new WordModel(word); } } // CONCATENATED MODULE: ./src/truncate-element.ts class truncate_element_TruncateElement extends HTMLElement { constructor() { super(); this._highlightQuery = []; this.config = { number: 0, more: "", less: "", completeWord: false, hashtag: false, hasLiteral: false, highlightCondition: 'exactly', highlightList: [{ name: '', color: '' }], mention: false, }; this.number = 100; this.more = "show"; this.less = "hide"; this.completeWord = false; this.hashtag = false; this.hasLiteral = false; this.highlightCondition = 'exactly'; this.highlightList = [{ name: '', color: '' }]; this.text = ""; /** to display show or hide button*/ this.truncated = false; this.mention = false; this.identifyLink = { hasQueryString: false, title: "", class: "", style: "text-decoration:none;color:blue;", domain: [], protocol: [], target: "_blank", enabled: false }; this.userModels = []; this.linkList = []; this.linkCount = 0; this.hashtagCount = 0; this.mentionCount = 0; this.connectedLoaded = false; this.dataLoaded = false; this.tempHtml = ""; this.wordArray = []; this.remainText = ""; this.fullText = ""; this.truncatedWord = { model: new WordModel(''), index: 0 }; this.processModels = []; window.trun = this; const observer = new MutationObserver(() => { if (this.connectedLoaded && !this.dataLoaded) { this.initialValues(); } }); // call 'observe' on that MutationObserver instance, // passing it the element to observe, and the options object observer.observe(this, { characterData: false, childList: true, attributes: false }); } addUserModel(...userModel) { this.userModels.push(...userModel.map(f => ({ model: f }))); } connectedCallback() { setTimeout(() => { this.initialValues(); }, 0); this.connectedLoaded = true; } attributeChangedCallback(attrName, _oldVal, _newVal) { if (this.connectedLoaded) { this.initialValues(attrName); } } static get observedAttributes() { return ['config']; } /** initial values */ initialValues(attrName = "config") { if (!this.innerHTML) { return; } if (!this.tempHtml) { this.tempHtml = this.innerHTML; } const conf = this.getAttribute(attrName); if (conf === null || conf === void 0 ? void 0 : conf.length) { this.config = JSON.parse(conf); this.number = this.config.number ? this.config.number : this.number; this.more = this.config.more ? this.config.more : this.more; this.less = this.config.less ? this.config.less : this.less; this._highlightQuery = this.config.highlightList ? this.config.highlightList : this._highlightQuery; this.highlightCondition = this.config.highlightCondition ? this.config.highlightCondition : this.highlightCondition; this.completeWord = this.config.completeWord ? this.config.completeWord : this.completeWord; this.hashtag = this.config.hashtag ? this.config.hashtag : this.hashtag; this.hasLiteral = this.config.hasLiteral ? this.config.hasLiteral : this.hasLiteral; this.mention = this.config.mention ? this.config.mention : this.mention; if (this.config.identifyLink) { this.identifyLink.enabled = this.config.identifyLink.enabled; this.identifyLink.title = this.config.identifyLink.title ? this.config.identifyLink.title : this.identifyLink.title; this.identifyLink.class = this.config.identifyLink.class ? this.config.identifyLink.class : this.identifyLink.class; this.identifyLink.style = this.config.identifyLink.style ? this.config.identifyLink.style : this.identifyLink.style; this.identifyLink.domain = this.config.identifyLink.domain ? this.config.identifyLink.domain : this.identifyLink.domain; this.identifyLink.protocol = this.config.identifyLink.protocol ? this.config.identifyLink.protocol : this.identifyLink.protocol; this.identifyLink.target = this.config.identifyLink.target ? this.config.identifyLink.target : this.identifyLink.target; this.identifyLink.hasQueryString = this.config.identifyLink.hasQueryString ? this.config.identifyLink.hasQueryString : this.identifyLink.hasQueryString; } } else return; this.main(this.tempHtml); this.dataLoaded = true; } /** main method, start working * @param text {string} */ main(text) { if (!this.hasLiteral) text = text.replace(/(\r\n\t|\r\n|\n|\r\t)/gm, ' '); this.text = text; if (text.length > this.number) { this.truncated = true; const wordCut = new WordCut(this.number); if (this.completeWord) text = wordCut.cut(text); else { text = text.substring(0, this.number); const last = this.text.split(" ")[text.split(" ").length - 1]; this.truncatedWord.model = this.reviewTruncatedWordHasLink(last); this.truncatedWord.index = text.split(" ").length - 1; } } // all necessary classes this.processModels = this.generateModels(); // just once calling each class const instances = this.callOnceClasses(); const cuttedArray = text.split(" "); const fullArray = this.text.split(" "); const _cuttedwords = this.createWordModelFromString(cuttedArray); const _fullwords = this.createWordModelFromString(fullArray); //process all attributes of config and user models defined for each word const remain = this.proccessOnWordModels(_cuttedwords, instances); const full = this.proccessOnWordModels(_fullwords, instances); if (remain.length) { this.remainText = remain.map(m => m.html ? m.html : m.word).join(" ") + " ... "; if (!this.completeWord) { const l = remain.pop(); if (l && this.truncatedWord.model.type.includes('link')) { const replacement = this.truncatedWord.model.word; this.remainText = this.remainText.replace(new RegExp(`<a href=${l.word} `), `<a href=${replacement} `); } } } else { this.remainText = text + " ... "; } if (full.length) [ this.fullText = full.map(m => m.html ? m.html : m.word).join(" ") ]; else { this.fullText = this.text; } this.truncated ? this.initialText(this.remainText, this.more) : this.initialText(this.fullText); //This class is placed in order to apply \n characters in html if (this.hasLiteral) this.style.whiteSpace = 'pre-line'; } /** * once calling each class * @returns */ callOnceClasses() { const instances = []; for (const gm of this.processModels) { instances.push(new gm.model(this.config)); } return instances; } /** * review if the word is truncated has link or not * @param word string * @returns WordModel */ reviewTruncatedWordHasLink(word) { const link = new Link(this.config); return link.process(new WordModel(word)); } /** * finally process all attributes of config and user models defined for each word * @param _words WordModel[] * @param instances ProccessModel[] * @returns WordModel[] */ proccessOnWordModels(_words, instances) { for (let word of _words) { for (const gm of instances) { if (word.length == 0) continue; word = gm.process(word); } } return _words; } /** * create WordModel class of each wrod * @param array {string[]} * @returns string[] */ createWordModelFromString(array) { const _words = []; const nullModel = new NullModel(this.config); for (const word of array) { _words.push(nullModel.process(word)); } return _words; } /** * generate necessary classes for each word * @returns GeneralModel[] */ generateModels() { var _a, _b, _c; const generalModels = []; if ((_a = this.config.highlightList) === null || _a === void 0 ? void 0 : _a.length) generalModels.push({ model: Highlight }); if ((_b = this.config.identifyLink) === null || _b === void 0 ? void 0 : _b.enabled) generalModels.push({ model: Link }); if (this.config.hashtag) generalModels.push({ model: Hashtag }); if (this.config.mention) generalModels.push({ model: Mention }); if ((_c = this.userModels) === null || _c === void 0 ? void 0 : _c.length) { this.userModels.forEach(user => { generalModels.push(user); }); } return generalModels; } /** * display text in first time * @param text * @param toggle */ initialText(text, toggle) { if (toggle) { const span = this.createSpan(toggle); this.innerHTML = text; this.appendChild(span); } else this.innerHTML = text; } /** * display string after click on toggle text * @param text string * @param mouseDown mouse event */ showText(text, mouseDown) { this.truncated = !this.truncated; const span = this.createSpan(this.truncated ? this.more : this.less); this.innerHTML = text; if (this.hasLiteral) this.style.whiteSpace = 'pre-line'; mouseDown.stopPropagation(); this.appendChild(span); } /** * create span for toggling * @param toggle custom word for toggle * @returns */ createSpan(toggle) { const span = document.createElement('span'); span.innerHTML = ' ' + toggle; span.style.color = '#ff00ff'; span.style.cursor = 'pointer'; span.classList.add("toggleText"); span.addEventListener('click', (event) => this.showText(this.truncated ? this.fullText : this.remainText, event)); return span; } } if (!customElements.get('truncate-element')) { customElements.define('truncate-element', truncate_element_TruncateElement); } // CONCATENATED MODULE: ./src/index.ts /* concated harmony reexport ProccessModel */__webpack_require__.d(__webpack_exports__, "ProccessModel", function() { return ProccessModel; }); /* concated harmony reexport WordModel */__webpack_require__.d(__webpack_exports__, "WordModel", function() { return WordModel; }); /* concated harmony reexport WordCut */__webpack_require__.d(__webpack_exports__, "WordCut", function() { return WordCut; }); /* concated harmony reexport Link */__webpack_require__.d(__webpack_exports__, "Link", function() { return Link; }); /* concated harmony reexport Mention */__webpack_require__.d(__webpack_exports__, "Mention", function() { return Mention; }); /* concated harmony reexport Hashtag */__webpack_require__.d(__webpack_exports__, "Hashtag", function() { return Hashtag; }); /* concated harmony reexport Highlight */__webpack_require__.d(__webpack_exports__, "Highlight", function() { return Highlight; }); /* concated harmony reexport NullModel */__webpack_require__.d(__webpack_exports__, "NullModel", function() { return NullModel; }); /* concated harmony reexport TruncateElement */__webpack_require__.d(__webpack_exports__, "TruncateElement", function() { return truncate_element_TruncateElement; }); /***/ }) /******/ ]); }); //# sourceMappingURL=truncate-element.js.map